зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
3ec5c00413
|
@ -84,13 +84,6 @@ exports['test exceptions'] = function(assert) {
|
|||
}
|
||||
};
|
||||
|
||||
exports['test opt version'] = function(assert) {
|
||||
let fixture = sandbox();
|
||||
assert.throws(function() {
|
||||
evaluate(fixture, 'let a = 2;', 'test.js', 1, '1.5');
|
||||
}, 'No let in js 1.5');
|
||||
};
|
||||
|
||||
exports['test load'] = function(assert) {
|
||||
let fixture = sandbox();
|
||||
load(fixture, fixturesURI + 'sandbox-normal.js');
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
|
@ -1,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
|
@ -39,7 +39,7 @@
|
|||
},
|
||||
"b2g_manifest": "aries.xml",
|
||||
"b2g_manifest_intree": true,
|
||||
"additional_source_tarballs": [],
|
||||
"additional_source_tarballs": ["backup-aries.tar.xz"],
|
||||
"gecko_l10n_root": "https://hg.mozilla.org/l10n-central",
|
||||
"gaia": {
|
||||
"l10n": {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
[
|
||||
{
|
||||
"size": 135359412,
|
||||
"digest": "45e677c9606cc4eec44ef4761df47ff431df1ffad17a5c6d21ce700a1c47f79e87a4aa9f30ae47ff060bd64f5b775d995780d88211f9a759ffa0d076beb4816b",
|
||||
"algorithm": "sha512",
|
||||
"filename": "backup-aries.tar.xz",
|
||||
"comment": "v18D"
|
||||
},
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
|
@ -5,12 +5,5 @@
|
|||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
|
@ -12,12 +12,5 @@
|
|||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -10,12 +10,5 @@
|
|||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -23,13 +23,6 @@
|
|||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -23,13 +23,6 @@
|
|||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -4,12 +4,4 @@
|
|||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
},
|
||||
{
|
||||
"size": 12057960,
|
||||
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gtk3.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
|
|
@ -31,7 +31,7 @@ from check_utils import get_all_toplevel_filenames
|
|||
|
||||
architecture_independent = set([ 'generic' ])
|
||||
all_architecture_names = set([ 'x86', 'x64', 'arm', 'arm64', 'mips32' ])
|
||||
all_shared_architecture_names = set([ 'x86_shared', 'arm', 'arm64', 'mips32' ])
|
||||
all_shared_architecture_names = set([ 'x86_shared', 'mips_shared', 'arm', 'arm64' ])
|
||||
|
||||
reBeforeArg = "(?<=[(,\s])"
|
||||
reArgType = "(?P<type>[\w\s:*&]+)"
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/ChromeNodeList.h"
|
||||
#include "mozilla/dom/ChromeNodeListBinding.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
already_AddRefed<ChromeNodeList>
|
||||
ChromeNodeList::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
nsIDocument* root = win ? win->GetExtantDoc() : nullptr;
|
||||
RefPtr<ChromeNodeList> list = new ChromeNodeList(root);
|
||||
return list.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
ChromeNodeList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ChromeNodeListBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
ChromeNodeList::Append(nsINode& aNode, ErrorResult& aError)
|
||||
{
|
||||
if (!aNode.IsContent()) {
|
||||
// nsINodeList deals with nsIContent objects only, so need to
|
||||
// filter out other nodes for now.
|
||||
aError.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
AppendElement(aNode.AsContent());
|
||||
}
|
||||
|
||||
void
|
||||
ChromeNodeList::Remove(nsINode& aNode, ErrorResult& aError)
|
||||
{
|
||||
if (!aNode.IsContent()) {
|
||||
aError.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveElement(aNode.AsContent());
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsContentList.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
class GlobalObject;
|
||||
|
||||
class ChromeNodeList final : public nsSimpleContentList
|
||||
{
|
||||
public:
|
||||
explicit ChromeNodeList(nsINode* aOwner)
|
||||
: nsSimpleContentList(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
static already_AddRefed<ChromeNodeList>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void Append(nsINode& aNode, ErrorResult& aError);
|
||||
void Remove(nsINode& aNode, ErrorResult& aError);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ScreenOrientation,
|
||||
|
|
|
@ -154,6 +154,7 @@ EXPORTS.mozilla.dom += [
|
|||
'BarProps.h',
|
||||
'BlobSet.h',
|
||||
'ChildIterator.h',
|
||||
'ChromeNodeList.h',
|
||||
'ChromeUtils.h',
|
||||
'Comment.h',
|
||||
'Console.h',
|
||||
|
@ -217,6 +218,7 @@ UNIFIED_SOURCES += [
|
|||
'Attr.cpp',
|
||||
'BarProps.cpp',
|
||||
'ChildIterator.cpp',
|
||||
'ChromeNodeList.cpp',
|
||||
'ChromeUtils.cpp',
|
||||
'Comment.cpp',
|
||||
'Console.cpp',
|
||||
|
|
|
@ -7372,6 +7372,7 @@ nsContentUtils::CallOnAllRemoteChildren(nsIDOMWindow* aWindow,
|
|||
void
|
||||
nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
|
||||
nsTArray<IPCDataTransfer>& aIPC,
|
||||
bool aInSyncMessage,
|
||||
mozilla::dom::nsIContentChild* aChild,
|
||||
mozilla::dom::nsIContentParent* aParent)
|
||||
{
|
||||
|
@ -7384,14 +7385,71 @@ nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables
|
|||
nsCOMPtr<nsISupports> genericItem;
|
||||
aTransferables->GetElementAt(i, getter_AddRefs(genericItem));
|
||||
nsCOMPtr<nsITransferable> transferable(do_QueryInterface(genericItem));
|
||||
TransferableToIPCTransferable(transferable, dt, aChild, aParent);
|
||||
TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild, aParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::SlurpFileToString(nsIFile* aFile, nsACString& aString)
|
||||
{
|
||||
aString.Truncate();
|
||||
|
||||
nsCOMPtr<nsIURI> fileURI;
|
||||
nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), aFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
fileURI,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = channel->Open2(getter_AddRefs(stream));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = NS_ConsumeStream(stream, UINT32_MAX, aString);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = stream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType)
|
||||
{
|
||||
nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
|
||||
if (!mime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = mime->GetTypeFromFile(aFile, aType);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return StringBeginsWith(aType, NS_LITERAL_CSTRING("image/"));
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
||||
IPCDataTransfer* aIPCDataTransfer,
|
||||
bool aInSyncMessage,
|
||||
mozilla::dom::nsIContentChild* aChild,
|
||||
mozilla::dom::nsIContentParent* aParent)
|
||||
{
|
||||
|
@ -7465,7 +7523,7 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
|||
int32_t stride;
|
||||
mozilla::UniquePtr<char[]> surfaceData =
|
||||
nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
|
||||
|
||||
|
||||
IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
|
||||
item->flavor() = nsCString(flavorStr);
|
||||
item->data() = nsCString(surfaceData.get(), length);
|
||||
|
@ -7485,6 +7543,22 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
|||
nsCOMPtr<BlobImpl> blobImpl;
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(data);
|
||||
if (file) {
|
||||
// If we can send this over as a blob, do so. Otherwise, we're
|
||||
// responding to a sync message and the child can't process the blob
|
||||
// constructor before processing our response, which would crash. In
|
||||
// that case, hope that the caller is nsClipboardProxy::GetData,
|
||||
// called from editor and send over images as raw data.
|
||||
if (aInSyncMessage) {
|
||||
nsAutoCString type;
|
||||
if (IsFileImage(file, type)) {
|
||||
IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
|
||||
item->flavor() = type;
|
||||
SlurpFileToString(file, item->data());
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
blobImpl = new BlobImplFile(file, false);
|
||||
ErrorResult rv;
|
||||
// Ensure that file data is cached no that the content process
|
||||
|
@ -7493,6 +7567,10 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
|||
blobImpl->GetLastModified(rv);
|
||||
blobImpl->LookupAndCacheIsDirectory();
|
||||
} else {
|
||||
if (aInSyncMessage) {
|
||||
// Can't do anything.
|
||||
continue;
|
||||
}
|
||||
blobImpl = do_QueryInterface(data);
|
||||
}
|
||||
if (blobImpl) {
|
||||
|
|
|
@ -2408,13 +2408,30 @@ public:
|
|||
CallOnRemoteChildFunction aCallback,
|
||||
void* aArg);
|
||||
|
||||
/**
|
||||
* Given an nsIFile, attempts to read it into aString.
|
||||
*
|
||||
* Note: Use sparingly! This causes main-thread I/O, which causes jank and all
|
||||
* other bad things.
|
||||
*/
|
||||
static nsresult SlurpFileToString(nsIFile* aFile, nsACString& aString);
|
||||
|
||||
/**
|
||||
* Returns true if the mime service thinks this file contains an image.
|
||||
*
|
||||
* The content type is returned in aType.
|
||||
*/
|
||||
static bool IsFileImage(nsIFile* aFile, nsACString& aType);
|
||||
|
||||
static void TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
|
||||
nsTArray<mozilla::dom::IPCDataTransfer>& aIPC,
|
||||
bool aInSyncMessage,
|
||||
mozilla::dom::nsIContentChild* aChild,
|
||||
mozilla::dom::nsIContentParent* aParent);
|
||||
|
||||
static void TransferableToIPCTransferable(nsITransferable* aTransferable,
|
||||
mozilla::dom::IPCDataTransfer* aIPCDataTransfer,
|
||||
bool aInSyncMessage,
|
||||
mozilla::dom::nsIContentChild* aChild,
|
||||
mozilla::dom::nsIContentParent* aParent);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ support-files =
|
|||
window_nsITextInputProcessor.xul
|
||||
title_window.xul
|
||||
|
||||
[test_bug120684.xul]
|
||||
[test_bug206691.xul]
|
||||
[test_bug339494.xul]
|
||||
[test_bug357450.xul]
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=120684
|
||||
-->
|
||||
<window title="Mozilla Bug 120684"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=120684"
|
||||
target="_blank">Mozilla Bug 120684</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug 120684 **/
|
||||
|
||||
var list = new ChromeNodeList();
|
||||
is(list.length, 0, "Length should be initially 0.");
|
||||
|
||||
ok(list instanceof NodeList, "ChromeNodeList object should be an instance of NodeList.");
|
||||
|
||||
try {
|
||||
list.append(document);
|
||||
ok(false, "should have throw!");
|
||||
} catch(ex) {
|
||||
ok(true, "ChromeNodeList supports only nsIContent objects for now.");
|
||||
}
|
||||
|
||||
try {
|
||||
list.remove(document);
|
||||
ok(false, "should have throw!");
|
||||
} catch(ex) {
|
||||
ok(true, "ChromeNodeList supports only nsIContent objects for now.");
|
||||
}
|
||||
is(list.length, 0, "Length should be 0.");
|
||||
|
||||
list.append(document.documentElement);
|
||||
is(list.length, 1, "Length should be 1.");
|
||||
is(list[0], document.documentElement);
|
||||
is(list[1], undefined);
|
||||
|
||||
// Removing element which isn't in the list shouldn't do anything.
|
||||
list.remove(document.createElement("foo"));
|
||||
is(list.length, 1, "Length should be 1.");
|
||||
is(list[0], document.documentElement);
|
||||
|
||||
list.remove(document.documentElement);
|
||||
is(list.length, 0, "Length should be 0.");
|
||||
is(list[0], undefined);
|
||||
|
||||
var e1 = document.createElement("foo");
|
||||
var e2 = document.createElement("foo");
|
||||
var e3 = document.createElement("foo");
|
||||
|
||||
list.append(e1);
|
||||
list.append(e2);
|
||||
list.append(e3);
|
||||
|
||||
is(list[0], e1);
|
||||
is(list[1], e2);
|
||||
is(list[2], e3);
|
||||
is(list.length, 3);
|
||||
|
||||
list.remove(e2);
|
||||
is(list[0], e1);
|
||||
is(list[1], e3);
|
||||
is(list[2], undefined);
|
||||
is(list.length, 2);
|
||||
|
||||
// A leak test.
|
||||
list.expando = list;
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
|
@ -537,44 +537,19 @@ DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
|
|||
|
||||
// We should use JSFunction objects for interface objects, but we need a custom
|
||||
// hasInstance hook because we have new interface objects on prototype chains of
|
||||
// old (XPConnect-based) bindings. Because Function.prototype.toString throws if
|
||||
// passed a non-Function object we also need to provide our own toString method
|
||||
// for interface objects.
|
||||
|
||||
static bool
|
||||
InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
|
||||
// old (XPConnect-based) bindings. We also need Xrays and arbitrary numbers of
|
||||
// reserved slots (e.g. for named constructors). So we define a custom
|
||||
// funToString ObjectOps member for interface objects.
|
||||
JSString*
|
||||
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
||||
unsigned /* indent */)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
if (!args.thisv().isObject()) {
|
||||
JS_ReportErrorNumber(cx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_CANT_CONVERT_TO, "null", "object");
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
|
||||
JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(thisObj, /* stopAtOuter = */ false));
|
||||
if (!obj) {
|
||||
JS_ReportError(cx, "Permission denied to access object");
|
||||
return false;
|
||||
}
|
||||
|
||||
const js::Class* clasp = js::GetObjectClass(obj);
|
||||
if (!IsDOMIfaceAndProtoClass(clasp)) {
|
||||
JS_ReportError(cx, "toString called on incompatible object");
|
||||
return false;
|
||||
}
|
||||
const js::Class* clasp = js::GetObjectClass(aObject);
|
||||
MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
|
||||
|
||||
const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(clasp);
|
||||
JS::Rooted<JSString*> str(cx,
|
||||
JS_NewStringCopyZ(cx,
|
||||
ifaceAndProtoJSClass->mToString));
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
return JS_NewStringCopyZ(aCx, ifaceAndProtoJSClass->mToString);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -647,15 +622,6 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
}
|
||||
|
||||
if (constructorClass) {
|
||||
// Have to shadow Function.prototype.toString, since that throws
|
||||
// on things that are not js::FunctionClass.
|
||||
JS::Rooted<JSFunction*> toString(cx,
|
||||
JS_DefineFunction(cx, constructor, "toString", InterfaceObjectToString,
|
||||
0, 0));
|
||||
if (!toString) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
|
||||
JSPROP_READONLY)) {
|
||||
return nullptr;
|
||||
|
@ -1570,22 +1536,6 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
|||
JSPROP_PERMANENT | JSPROP_READONLY,
|
||||
desc, cacheOnHolder);
|
||||
}
|
||||
|
||||
if (IdEquals(id, "toString") && !JS_ObjectIsFunction(cx, obj)) {
|
||||
MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
|
||||
|
||||
JS::Rooted<JSFunction*> toString(cx, JS_NewFunction(cx, InterfaceObjectToString, 0, 0, "toString"));
|
||||
if (!toString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cacheOnHolder = true;
|
||||
|
||||
FillPropertyDescriptor(desc, wrapper, 0,
|
||||
JS::ObjectValue(*JS_GetFunctionObject(toString)));
|
||||
|
||||
return JS_WrapPropertyDescriptor(cx, desc);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(IsInterfacePrototype(type));
|
||||
|
||||
|
|
|
@ -3359,6 +3359,10 @@ void
|
|||
DeprecationWarning(JSContext* aCx, JSObject* aObject,
|
||||
nsIDocument::DeprecatedOperations aOperation);
|
||||
|
||||
// A callback to perform funToString on an interface object
|
||||
JSString*
|
||||
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
||||
unsigned /* indent */);
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -453,7 +453,8 @@ class CGDOMJSClass(CGThing):
|
|||
nullptr, /* unwatch */
|
||||
nullptr, /* getElements */
|
||||
nullptr, /* enumerate */
|
||||
mozilla::dom::ObjectToOuterObjectValue /* thisValue */
|
||||
mozilla::dom::ObjectToOuterObjectValue, /* thisValue */
|
||||
nullptr, /* funToString */
|
||||
}
|
||||
""",
|
||||
objectMoved=objectMovedHook)
|
||||
|
@ -730,7 +731,21 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
nullptr, /* trace */
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
JS_NULL_OBJECT_OPS
|
||||
{
|
||||
nullptr, /* lookupProperty */
|
||||
nullptr, /* defineProperty */
|
||||
nullptr, /* hasProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* getOwnPropertyDescriptor */
|
||||
nullptr, /* deleteProperty */
|
||||
nullptr, /* watch */
|
||||
nullptr, /* unwatch */
|
||||
nullptr, /* getElements */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* thisObject */
|
||||
InterfaceObjectToString, /* funToString */
|
||||
}
|
||||
},
|
||||
eInterface,
|
||||
${hooks},
|
||||
|
|
|
@ -31,6 +31,15 @@ catch (e) {
|
|||
ok(false, "Stringifying a DOM interface object shouldn't throw.");
|
||||
}
|
||||
|
||||
try {
|
||||
eventTargetToString = Function.prototype.toString.call(EventTarget);
|
||||
is(eventTargetToString, nativeToString,
|
||||
"Stringifying a DOM interface object via Function.prototype.toString " +
|
||||
"should return the same string as stringifying a native function.");
|
||||
}
|
||||
catch (e) {
|
||||
ok(false, "Stringifying a DOM interface object shouldn't throw.");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -349,11 +349,15 @@ TypeUtils::ToInternalRequest(const CacheRequest& aIn)
|
|||
RefPtr<InternalHeaders> internalHeaders =
|
||||
ToInternalHeaders(aIn.headers(), aIn.headersGuard());
|
||||
ErrorResult result;
|
||||
internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
|
||||
// Be careful to fill the headers before setting the guard in order to
|
||||
// correctly re-create the original headers.
|
||||
internalRequest->Headers()->Fill(*internalHeaders, result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
|
||||
internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
|
||||
|
||||
internalRequest->SetBody(stream);
|
||||
|
|
|
@ -303,7 +303,11 @@ FetchDriver::HttpFetch()
|
|||
// Set the same headers.
|
||||
nsAutoTArray<InternalHeaders::Entry, 5> headers;
|
||||
mRequest->Headers()->GetEntries(headers);
|
||||
bool hasAccept = false;
|
||||
for (uint32_t i = 0; i < headers.Length(); ++i) {
|
||||
if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) {
|
||||
hasAccept = true;
|
||||
}
|
||||
if (headers[i].mValue.IsEmpty()) {
|
||||
httpChan->SetEmptyRequestHeader(headers[i].mName);
|
||||
} else {
|
||||
|
@ -311,6 +315,12 @@ FetchDriver::HttpFetch()
|
|||
}
|
||||
}
|
||||
|
||||
if (!hasAccept) {
|
||||
httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
|
||||
NS_LITERAL_CSTRING("*/*"),
|
||||
false /* merge */);
|
||||
}
|
||||
|
||||
// Step 2. Set the referrer.
|
||||
nsAutoString referrer;
|
||||
mRequest->GetReferrer(referrer);
|
||||
|
|
|
@ -151,13 +151,8 @@ InternalHeaders::Clear()
|
|||
void
|
||||
InternalHeaders::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
|
||||
{
|
||||
// Rather than re-validate all current headers, just require code to set
|
||||
// this prior to populating the InternalHeaders object. Allow setting immutable
|
||||
// late, though, as that is pretty much required to have a useful, immutable
|
||||
// headers object.
|
||||
if (aGuard != HeadersGuardEnum::Immutable && mList.Length() > 0) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
// The guard is only checked during ::Set() and ::Append() in the spec. It
|
||||
// does not require revalidating headers already set.
|
||||
mGuard = aGuard;
|
||||
}
|
||||
|
||||
|
|
|
@ -1404,11 +1404,11 @@ HTMLMediaElement::CurrentTime() const
|
|||
return stream->StreamTimeToSeconds(stream->GetCurrentTime());
|
||||
}
|
||||
|
||||
if (mDecoder) {
|
||||
if (mDefaultPlaybackStartPosition == 0.0 && mDecoder) {
|
||||
return mDecoder->GetCurrentTime();
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
return mDefaultPlaybackStartPosition;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP HTMLMediaElement::GetCurrentTime(double* aCurrentTime)
|
||||
|
@ -1484,19 +1484,11 @@ HTMLMediaElement::Seek(double aTime,
|
|||
StopSuspendingAfterFirstFrame();
|
||||
|
||||
if (mSrcStream) {
|
||||
// do nothing since streams aren't seekable; we effectively clamp to
|
||||
// the current time.
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
// do nothing since media streams have an empty Seekable range.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mPlayed) {
|
||||
LOG(LogLevel::Debug, ("HTMLMediaElement::mPlayed not available."));
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentPlayRangeStart != -1.0) {
|
||||
if (mPlayed && mCurrentPlayRangeStart != -1.0) {
|
||||
double rangeEndTime = CurrentTime();
|
||||
LOG(LogLevel::Debug, ("%p Adding \'played\' a range : [%f, %f]", this, mCurrentPlayRangeStart, rangeEndTime));
|
||||
// Multiple seek without playing, or seek while playing.
|
||||
|
@ -1508,15 +1500,14 @@ HTMLMediaElement::Seek(double aTime,
|
|||
mCurrentPlayRangeStart = -1.0;
|
||||
}
|
||||
|
||||
if (!mDecoder) {
|
||||
LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) failed: no decoder", this, aTime));
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||
mDefaultPlaybackStartPosition = aTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||
LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) failed: no source", this, aTime));
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
if (!mDecoder) {
|
||||
// mDecoder must always be set in order to reach this point.
|
||||
NS_ASSERTION(mDecoder, "SetCurrentTime failed: no decoder");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2114,7 +2105,8 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
mMediaStreamTrackListener(nullptr),
|
||||
mElementInTreeState(ELEMENT_NOT_INTREE),
|
||||
mHasUserInteraction(false),
|
||||
mFirstFrameLoaded(false)
|
||||
mFirstFrameLoaded(false),
|
||||
mDefaultPlaybackStartPosition(0.0)
|
||||
{
|
||||
if (!gMediaElementLog) {
|
||||
gMediaElementLog = PR_NewLogModule("nsMediaElement");
|
||||
|
@ -3440,6 +3432,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
|||
// We are a video element playing video so update the screen wakelock
|
||||
NotifyOwnerDocumentActivityChangedInternal();
|
||||
}
|
||||
|
||||
if (mDefaultPlaybackStartPosition > 0) {
|
||||
SetCurrentTime(mDefaultPlaybackStartPosition);
|
||||
mDefaultPlaybackStartPosition = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::FirstFrameLoaded()
|
||||
|
|
|
@ -1516,6 +1516,11 @@ private:
|
|||
|
||||
// True if the first frame has been successfully loaded.
|
||||
bool mFirstFrameLoaded;
|
||||
|
||||
// Media elements also have a default playback start position, which must
|
||||
// initially be set to zero seconds. This time is used to allow the element to
|
||||
// be seeked even before the media is loaded.
|
||||
double mDefaultPlaybackStartPosition;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -59,7 +59,7 @@ MediaDocumentStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ct
|
|||
return mNextStream->OnStartRequest(request, ctxt);
|
||||
}
|
||||
|
||||
return NS_BINDING_ABORTED;
|
||||
return NS_ERROR_PARSED_DATA_CACHED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -1986,21 +1986,21 @@ private:
|
|||
}
|
||||
|
||||
size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
|
||||
nsAutoArrayPtr<char> compressed(new (fallible) char[compressedLength]);
|
||||
UniqueFreePtr<uint8_t> compressed(
|
||||
static_cast<uint8_t*>(malloc(compressedLength)));
|
||||
if (NS_WARN_IF(!compressed)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
|
||||
uncompressedLength, compressed.get(),
|
||||
uncompressedLength,
|
||||
reinterpret_cast<char*>(compressed.get()),
|
||||
&compressedLength);
|
||||
|
||||
std::pair<const void *, int> data(static_cast<void*>(compressed.get()),
|
||||
int(compressedLength));
|
||||
std::pair<uint8_t *, int> data(compressed.release(),
|
||||
int(compressedLength));
|
||||
|
||||
// XXX This copies the buffer again... There doesn't appear to be any way to
|
||||
// preallocate space and write directly to a BlobVariant at the moment.
|
||||
nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
|
||||
nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
|
||||
|
||||
result.forget(aResult);
|
||||
return NS_OK;
|
||||
|
|
|
@ -2828,7 +2828,7 @@ ContentParent::RecvGetClipboard(nsTArray<nsCString>&& aTypes,
|
|||
|
||||
clipboard->GetData(trans, aWhichClipboard);
|
||||
nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer,
|
||||
nullptr, this);
|
||||
true, nullptr, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5272,6 +5272,7 @@ ContentParent::MaybeInvokeDragSession(TabParent* aParent)
|
|||
transfer->GetTransferables(lc);
|
||||
nsContentUtils::TransferablesToIPCTransferables(transferables,
|
||||
dataTransfers,
|
||||
false,
|
||||
nullptr,
|
||||
this);
|
||||
uint32_t action;
|
||||
|
|
|
@ -23,6 +23,17 @@ runWithMSE(function(ms, el) {
|
|||
ok(true, "Receive a sourceopen event");
|
||||
var audiosb = ms.addSourceBuffer("audio/mp4");
|
||||
var videosb = ms.addSourceBuffer("video/mp4");
|
||||
el.addEventListener("error", function(e) {
|
||||
ok(false, "should not fire '" + e + "' event");
|
||||
});
|
||||
is(el.readyState, el.HAVE_NOTHING, "readyState is HAVE_NOTHING");
|
||||
try {
|
||||
el.currentTime = 3;
|
||||
} catch (e) {
|
||||
ok(false, "should not throw '" + e + "' exception");
|
||||
}
|
||||
is(el.currentTime, 3, "currentTime is default playback start position");
|
||||
is(el.seeking, false, "seek not started with HAVE_NOTHING");
|
||||
fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
|
||||
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
|
||||
.then(once.bind(null, el, 'loadedmetadata'))
|
||||
|
@ -30,6 +41,9 @@ runWithMSE(function(ms, el) {
|
|||
var p = once(el, 'seeking');
|
||||
el.play();
|
||||
el.currentTime = 5;
|
||||
is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA");
|
||||
is(el.seeking, true, "seek not started with HAVE_METADATA");
|
||||
is(el.currentTime, 5, "currentTime is seek position");
|
||||
return p;
|
||||
})
|
||||
.then(function() {
|
||||
|
|
|
@ -134,18 +134,51 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
|
|||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
RefPtr<PlatformDecoderModule> current = (mEMEPDM && aConfig.mCrypto.mValid)
|
||||
? mEMEPDM : GetDecoder(aConfig.mMimeType);
|
||||
bool isEncrypted = mEMEPDM && aConfig.mCrypto.mValid;
|
||||
|
||||
if (!current) {
|
||||
return nullptr;
|
||||
if (isEncrypted) {
|
||||
return CreateDecoderWithPDM(mEMEPDM,
|
||||
aConfig,
|
||||
aTaskQueue,
|
||||
aCallback,
|
||||
aLayersBackend,
|
||||
aImageContainer);
|
||||
}
|
||||
|
||||
for (auto& current : mCurrentPDMs) {
|
||||
if (!current->SupportsMimeType(aConfig.mMimeType)) {
|
||||
continue;
|
||||
}
|
||||
RefPtr<MediaDataDecoder> m =
|
||||
CreateDecoderWithPDM(current,
|
||||
aConfig,
|
||||
aTaskQueue,
|
||||
aCallback,
|
||||
aLayersBackend,
|
||||
aImageContainer);
|
||||
if (m) {
|
||||
return m.forget();
|
||||
}
|
||||
}
|
||||
NS_WARNING("Unable to create a decoder, no platform found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
|
||||
const TrackInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
MOZ_ASSERT(aPDM);
|
||||
RefPtr<MediaDataDecoder> m;
|
||||
|
||||
if (aConfig.GetAsAudioInfo()) {
|
||||
m = current->CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
m = aPDM->CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
|
@ -165,7 +198,7 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
|
|||
|
||||
if (H264Converter::IsH264(aConfig)) {
|
||||
RefPtr<H264Converter> h
|
||||
= new H264Converter(current,
|
||||
= new H264Converter(aPDM,
|
||||
*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
|
@ -179,11 +212,11 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
|
|||
m = h.forget();
|
||||
}
|
||||
} else {
|
||||
m = current->CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
callback);
|
||||
m = aPDM->CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
callback);
|
||||
}
|
||||
|
||||
if (callbackWrapper && m) {
|
||||
|
|
|
@ -53,6 +53,13 @@ private:
|
|||
bool StartupPDM(PlatformDecoderModule* aPDM);
|
||||
// Returns the first PDM in our list supporting the mimetype.
|
||||
already_AddRefed<PlatformDecoderModule> GetDecoder(const nsACString& aMimeType);
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
|
||||
const TrackInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer);
|
||||
|
||||
// PDM pref caches...
|
||||
static bool sUseBlankDecoder;
|
||||
|
|
|
@ -214,6 +214,8 @@ public:
|
|||
// that the format of the next input sample is about to change.
|
||||
// If video decoder, aConfig will be a VideoInfo object.
|
||||
// If audio decoder, aConfig will be a AudioInfo object.
|
||||
// It is not safe to store a reference to this object and the decoder must
|
||||
// make a copy.
|
||||
virtual nsresult ConfigurationChanged(const TrackInfo& aConfig)
|
||||
{
|
||||
return NS_OK;
|
||||
|
|
|
@ -247,22 +247,30 @@ public:
|
|||
int32_t size;
|
||||
NS_ENSURE_SUCCESS(rv = aInfo->Size(&size), rv);
|
||||
|
||||
const int32_t numFrames = (size / numChannels) / 2;
|
||||
AudioDataValue* audio = new AudioDataValue[size];
|
||||
PodCopy(audio, static_cast<AudioDataValue*>(aBuffer), size);
|
||||
|
||||
int32_t offset;
|
||||
NS_ENSURE_SUCCESS(rv = aInfo->Offset(&offset), rv);
|
||||
|
||||
#ifdef MOZ_SAMPLE_TYPE_S16
|
||||
int32_t numSamples = size / 2;
|
||||
#else
|
||||
#error We only support 16-bit integer PCM
|
||||
#endif
|
||||
|
||||
const int32_t numFrames = numSamples / numChannels;
|
||||
AudioDataValue* audio = new AudioDataValue[numSamples];
|
||||
|
||||
uint8_t* bufferStart = static_cast<uint8_t*>(aBuffer) + offset;
|
||||
PodCopy(audio, reinterpret_cast<AudioDataValue*>(bufferStart), numSamples);
|
||||
|
||||
int64_t presentationTimeUs;
|
||||
NS_ENSURE_SUCCESS(rv = aInfo->PresentationTimeUs(&presentationTimeUs), rv);
|
||||
|
||||
RefPtr<AudioData> data = new AudioData(offset, presentationTimeUs,
|
||||
aDuration.ToMicroseconds(),
|
||||
numFrames,
|
||||
audio,
|
||||
numChannels,
|
||||
sampleRate);
|
||||
RefPtr<AudioData> data = new AudioData(0, presentationTimeUs,
|
||||
aDuration.ToMicroseconds(),
|
||||
numFrames,
|
||||
audio,
|
||||
numChannels,
|
||||
sampleRate);
|
||||
INVOKE_CALLBACK(Output, data);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@ WMFDecoderModule::Init()
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
sDXVAEnabled = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
|
||||
sIsIntelDecoderEnabled = Preferences::GetBool("media.webm.intel_decoder.enabled", false);
|
||||
Preferences::AddBoolVarCache(&sIsIntelDecoderEnabled,
|
||||
"media.webm.intel_decoder.enabled");
|
||||
sLowLatencyMFTEnabled = Preferences::GetBool("media.wmf.low-latency.enabled", false);
|
||||
SetNumOfDecoderThreads();
|
||||
}
|
||||
|
@ -139,10 +140,13 @@ CanCreateMFTDecoder(const GUID& aGuid)
|
|||
if (FAILED(wmf::MFStartup())) {
|
||||
return false;
|
||||
}
|
||||
RefPtr<MFTDecoder> decoder(new MFTDecoder());
|
||||
bool hasH264 = SUCCEEDED(decoder->Create(aGuid));
|
||||
bool hasdecoder = false;
|
||||
{
|
||||
RefPtr<MFTDecoder> decoder(new MFTDecoder());
|
||||
hasdecoder = SUCCEEDED(decoder->Create(aGuid));
|
||||
}
|
||||
wmf::MFShutdown();
|
||||
return hasH264;
|
||||
return hasdecoder;
|
||||
}
|
||||
|
||||
template<const GUID& aGuid>
|
||||
|
@ -173,7 +177,7 @@ WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType)
|
|||
CanCreateWMFDecoder<CLSID_CMP3DecMediaObject>()) {
|
||||
return true;
|
||||
}
|
||||
if (sIsIntelDecoderEnabled) {
|
||||
if (sIsIntelDecoderEnabled && sDXVAEnabled) {
|
||||
if (aMimeType.EqualsLiteral("video/webm; codecs=vp8") &&
|
||||
CanCreateWMFDecoder<CLSID_WebmMfVp8Dec>()) {
|
||||
return true;
|
||||
|
|
|
@ -232,4 +232,27 @@ WMFMediaDataDecoder::IsHardwareAccelerated(nsACString& aFailureReason) const {
|
|||
return mMFTManager && mMFTManager->IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
WMFMediaDataDecoder::ConfigurationChanged(const TrackInfo& aConfig)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<UniquePtr<TrackInfo>&&>(
|
||||
this,
|
||||
&WMFMediaDataDecoder::ProcessConfigurationChanged,
|
||||
aConfig.Clone());
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
WMFMediaDataDecoder::ProcessConfigurationChanged(UniquePtr<TrackInfo>&& aConfig)
|
||||
{
|
||||
if (mMFTManager) {
|
||||
mMFTManager->ConfigurationChanged(*aConfig);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -52,6 +52,8 @@ public:
|
|||
|
||||
virtual TrackInfo::TrackType GetType() = 0;
|
||||
|
||||
virtual void ConfigurationChanged(const TrackInfo& aConfig) {}
|
||||
|
||||
protected:
|
||||
// IMFTransform wrapper that performs the decoding.
|
||||
RefPtr<MFTDecoder> mDecoder;
|
||||
|
@ -81,6 +83,8 @@ public:
|
|||
|
||||
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
|
||||
|
||||
nsresult ConfigurationChanged(const TrackInfo& aConfig) override;
|
||||
|
||||
private:
|
||||
|
||||
// Called on the task queue. Inserts the sample into the decoder, and
|
||||
|
@ -101,6 +105,10 @@ private:
|
|||
|
||||
void ProcessShutdown();
|
||||
|
||||
// Called on the task queue. Tell the MFT that the next Input will have a
|
||||
// different configuration (typically resolution change).
|
||||
void ProcessConfigurationChanged(UniquePtr<TrackInfo>&& aConfig);
|
||||
|
||||
RefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames)
|
|||
}
|
||||
|
||||
HRESULT
|
||||
GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
|
||||
GetDefaultStride(IMFMediaType *aType, uint32_t aWidth, uint32_t* aOutStride)
|
||||
{
|
||||
// Try to get the default stride from the media type.
|
||||
HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
|
||||
|
@ -49,16 +49,11 @@ GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
|
|||
|
||||
// Stride attribute not set, calculate it.
|
||||
GUID subtype = GUID_NULL;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
|
||||
hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
|
||||
hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, aWidth, (LONG*)(aOutStride));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
return hr;
|
||||
|
|
|
@ -37,7 +37,7 @@ HRESULT
|
|||
HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames);
|
||||
|
||||
HRESULT
|
||||
GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride);
|
||||
GetDefaultStride(IMFMediaType *aType, uint32_t aWidth, uint32_t* aOutStride);
|
||||
|
||||
int32_t
|
||||
MFOffsetToInt32(const MFOffset& aOffset);
|
||||
|
|
|
@ -73,7 +73,9 @@ WMFVideoMFTManager::WMFVideoMFTManager(
|
|||
mozilla::layers::LayersBackend aLayersBackend,
|
||||
mozilla::layers::ImageContainer* aImageContainer,
|
||||
bool aDXVAEnabled)
|
||||
: mImageContainer(aImageContainer)
|
||||
: mVideoInfo(aConfig)
|
||||
, mVideoStride(0)
|
||||
, mImageContainer(aImageContainer)
|
||||
, mDXVAEnabled(aDXVAEnabled)
|
||||
, mLayersBackend(aLayersBackend)
|
||||
// mVideoStride, mVideoWidth, mVideoHeight, mUseHwAccel are initialized in
|
||||
|
@ -252,13 +254,6 @@ WMFVideoMFTManager::InitInternal(bool aForceD3D9)
|
|||
|
||||
LOG("Video Decoder initialized, Using DXVA: %s", (mUseHwAccel ? "Yes" : "No"));
|
||||
|
||||
// Just in case ConfigureVideoFrameGeometry() does not set these
|
||||
mVideoInfo = VideoInfo();
|
||||
mVideoStride = 0;
|
||||
mVideoWidth = 0;
|
||||
mVideoHeight = 0;
|
||||
mPictureRegion.SetEmpty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -372,26 +367,17 @@ WMFVideoMFTManager::ConfigureVideoFrameGeometry()
|
|||
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL);
|
||||
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL);
|
||||
|
||||
nsIntRect pictureRegion;
|
||||
hr = GetPictureRegion(mediaType, pictureRegion);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
UINT32 width = 0, height = 0;
|
||||
hr = MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
uint32_t aspectNum = 0, aspectDenom = 0;
|
||||
hr = MFGetAttributeRatio(mediaType,
|
||||
MF_MT_PIXEL_ASPECT_RATIO,
|
||||
&aspectNum,
|
||||
&aspectDenom);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
||||
mVideoInfo.mImage.width = width;
|
||||
mVideoInfo.mImage.height = height;
|
||||
nsIntRect pictureRegion = mVideoInfo.mImage;
|
||||
// Calculate and validate the picture region and frame dimensions after
|
||||
// scaling by the pixel aspect ratio.
|
||||
nsIntSize frameSize = nsIntSize(width, height);
|
||||
nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
|
||||
ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
|
||||
nsIntSize displaySize = nsIntSize(mVideoInfo.mDisplay.width, mVideoInfo.mDisplay.height);
|
||||
if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) {
|
||||
// Video track's frame sizes will overflow. Ignore the video track.
|
||||
return E_FAIL;
|
||||
|
@ -403,18 +389,13 @@ WMFVideoMFTManager::ConfigureVideoFrameGeometry()
|
|||
}
|
||||
|
||||
// Success! Save state.
|
||||
mVideoInfo.mDisplay = displaySize;
|
||||
GetDefaultStride(mediaType, &mVideoStride);
|
||||
mVideoWidth = width;
|
||||
mVideoHeight = height;
|
||||
mPictureRegion = pictureRegion;
|
||||
GetDefaultStride(mediaType, width, &mVideoStride);
|
||||
|
||||
LOG("WMFVideoMFTManager frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d) PAR=%d:%d",
|
||||
LOG("WMFVideoMFTManager frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d)",
|
||||
width, height,
|
||||
mVideoStride,
|
||||
mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height,
|
||||
displaySize.width, displaySize.height,
|
||||
aspectNum, aspectDenom);
|
||||
pictureRegion.x, pictureRegion.y, pictureRegion.width, pictureRegion.height,
|
||||
mVideoInfo.mDisplay.width, mVideoInfo.mDisplay.height);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -456,25 +437,28 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
|
|||
// i.e., Y, then V, then U.
|
||||
VideoData::YCbCrBuffer b;
|
||||
|
||||
uint32_t videoWidth = mVideoInfo.mImage.width;
|
||||
uint32_t videoHeight = mVideoInfo.mImage.height;
|
||||
|
||||
// Y (Y') plane
|
||||
b.mPlanes[0].mData = data;
|
||||
b.mPlanes[0].mStride = stride;
|
||||
b.mPlanes[0].mHeight = mVideoHeight;
|
||||
b.mPlanes[0].mWidth = mVideoWidth;
|
||||
b.mPlanes[0].mHeight = videoHeight;
|
||||
b.mPlanes[0].mWidth = videoWidth;
|
||||
b.mPlanes[0].mOffset = 0;
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
// The V and U planes are stored 16-row-aligned, so we need to add padding
|
||||
// to the row heights to ensure the Y'CbCr planes are referenced properly.
|
||||
uint32_t padding = 0;
|
||||
if (mVideoHeight % 16 != 0) {
|
||||
padding = 16 - (mVideoHeight % 16);
|
||||
if (videoHeight % 16 != 0) {
|
||||
padding = 16 - (videoHeight % 16);
|
||||
}
|
||||
uint32_t y_size = stride * (mVideoHeight + padding);
|
||||
uint32_t v_size = stride * (mVideoHeight + padding) / 4;
|
||||
uint32_t y_size = stride * (videoHeight + padding);
|
||||
uint32_t v_size = stride * (videoHeight + padding) / 4;
|
||||
uint32_t halfStride = (stride + 1) / 2;
|
||||
uint32_t halfHeight = (mVideoHeight + 1) / 2;
|
||||
uint32_t halfWidth = (mVideoWidth + 1) / 2;
|
||||
uint32_t halfHeight = (videoHeight + 1) / 2;
|
||||
uint32_t halfWidth = (videoWidth + 1) / 2;
|
||||
|
||||
// U plane (Cb)
|
||||
b.mPlanes[1].mData = data + y_size + v_size;
|
||||
|
@ -503,7 +487,7 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
|
|||
VideoData::SetVideoDataToImage(image,
|
||||
mVideoInfo,
|
||||
b,
|
||||
mPictureRegion,
|
||||
mVideoInfo.mImage,
|
||||
false);
|
||||
|
||||
RefPtr<VideoData> v =
|
||||
|
@ -515,7 +499,7 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
|
|||
image.forget(),
|
||||
false,
|
||||
-1,
|
||||
mPictureRegion);
|
||||
mVideoInfo.mImage);
|
||||
|
||||
v.forget(aOutVideoData);
|
||||
return S_OK;
|
||||
|
@ -536,7 +520,7 @@ WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
|
|||
|
||||
RefPtr<Image> image;
|
||||
hr = mDXVA2Manager->CopyToImage(aSample,
|
||||
mPictureRegion,
|
||||
mVideoInfo.mImage,
|
||||
mImageContainer,
|
||||
getter_AddRefs(image));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
|
@ -554,7 +538,7 @@ WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
|
|||
image.forget(),
|
||||
false,
|
||||
-1,
|
||||
mPictureRegion);
|
||||
mVideoInfo.mImage);
|
||||
|
||||
NS_ENSURE_TRUE(v, E_FAIL);
|
||||
v.forget(aOutVideoData);
|
||||
|
@ -632,4 +616,11 @@ WMFVideoMFTManager::IsHardwareAccelerated(nsACString& aFailureReason) const
|
|||
return mDecoder && mUseHwAccel;
|
||||
}
|
||||
|
||||
void
|
||||
WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
|
||||
{
|
||||
MOZ_ASSERT(aConfig.GetAsVideoInfo());
|
||||
mVideoInfo = *aConfig.GetAsVideoInfo();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
return TrackInfo::kVideoTrack;
|
||||
}
|
||||
|
||||
void ConfigurationChanged(const TrackInfo& aConfig) override;
|
||||
|
||||
private:
|
||||
|
||||
bool InitializeDXVA(bool aForceD3D9);
|
||||
|
@ -62,9 +64,6 @@ private:
|
|||
// Video frame geometry.
|
||||
VideoInfo mVideoInfo;
|
||||
uint32_t mVideoStride;
|
||||
uint32_t mVideoWidth;
|
||||
uint32_t mVideoHeight;
|
||||
nsIntRect mPictureRegion;
|
||||
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
|
||||
|
|
|
@ -22,6 +22,7 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM,
|
|||
FlushableTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mPDM(aPDM)
|
||||
, mOriginalConfig(aConfig)
|
||||
, mCurrentConfig(aConfig)
|
||||
, mLayersBackend(aLayersBackend)
|
||||
, mImageContainer(aImageContainer)
|
||||
|
@ -53,14 +54,10 @@ H264Converter::Init()
|
|||
nsresult
|
||||
H264Converter::Input(MediaRawData* aSample)
|
||||
{
|
||||
if (!mNeedAVCC) {
|
||||
if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
|
||||
// We need AVCC content to be able to later parse the SPS.
|
||||
// This is a no-op if the data is already AVCC.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mInitPromiseRequest.Exists()) {
|
||||
|
@ -84,6 +81,11 @@ H264Converter::Input(MediaRawData* aSample)
|
|||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!mNeedAVCC &&
|
||||
!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aSample->mExtraData = mCurrentConfig.mExtraData;
|
||||
|
||||
return mDecoder->Input(aSample);
|
||||
|
@ -138,7 +140,7 @@ H264Converter::CreateDecoder()
|
|||
}
|
||||
UpdateConfigFromExtraData(mCurrentConfig.mExtraData);
|
||||
|
||||
mDecoder = mPDM->CreateVideoDecoder(mCurrentConfig,
|
||||
mDecoder = mPDM->CreateVideoDecoder(mNeedAVCC ? mCurrentConfig : mOriginalConfig,
|
||||
mLayersBackend,
|
||||
mImageContainer,
|
||||
mVideoTaskQueue,
|
||||
|
|
|
@ -53,6 +53,7 @@ private:
|
|||
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
|
||||
|
||||
RefPtr<PlatformDecoderModule> mPDM;
|
||||
const VideoInfo& mOriginalConfig;
|
||||
VideoInfo mCurrentConfig;
|
||||
layers::LayersBackend mLayersBackend;
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
|
|
|
@ -768,6 +768,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
|||
tags=capturestream
|
||||
[test_resume.html]
|
||||
skip-if = true # bug 1021673
|
||||
[test_seek_nosrc.html]
|
||||
[test_seek_out_of_range.html]
|
||||
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_seek-1.html]
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Media test: seek tests</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var SEEK_TIME = 3.5;
|
||||
var seekStarted = false;
|
||||
var seekCompleted = false;
|
||||
var metadata = false;
|
||||
|
||||
var v = document.createElement('video');
|
||||
document.body.appendChild(v);
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
v.remove();
|
||||
});
|
||||
|
||||
try {
|
||||
v.currentTime = SEEK_TIME;
|
||||
} catch (e) {
|
||||
ok(false, "should not fire '" + e + "' event");
|
||||
}
|
||||
is(v.readyState, v.HAVE_NOTHING, "readyState is HAVE_NOTHING");
|
||||
ok(!v.seeking, "can't be seeking prior src defined");
|
||||
is(v.currentTime, SEEK_TIME, "currentTime is default playback start position");
|
||||
once(v, "seeking", function() {
|
||||
seekStarted = true;
|
||||
});
|
||||
once(v, "seeked", function() {
|
||||
seekCompleted = true;
|
||||
});
|
||||
once(v, "loadedmetadata", function() {
|
||||
metadata = true;
|
||||
ok(v.seeking, "element is seeking once readyState is HAVE_METADATA");
|
||||
});
|
||||
once(v, "ended", function() {
|
||||
ok(seekStarted, "seek should have started");
|
||||
ok(seekCompleted, "seek should have completed");
|
||||
ok(metadata, "loadedmetadata fired");
|
||||
ok(v.currentTime >= SEEK_TIME, "currentTime should be after seek time");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
v.src = "seek.webm";
|
||||
v.play();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -177,25 +177,25 @@ AudioNode::DisconnectFromGraph()
|
|||
DestroyMediaStream();
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode*
|
||||
AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
uint32_t aInput, ErrorResult& aRv)
|
||||
{
|
||||
if (aOutput >= NumberOfOutputs() ||
|
||||
aInput >= aDestination.NumberOfInputs()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Context() != aDestination.Context()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput, aOutput) !=
|
||||
nsTArray<AudioNode::InputNode>::NoIndex) {
|
||||
// connection already exists.
|
||||
return;
|
||||
return &aDestination;
|
||||
}
|
||||
|
||||
// The MediaStreamGraph will handle cycle detection. We don't need to do it
|
||||
|
@ -220,6 +220,8 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
|
|||
|
||||
// This connection may have connected a panner and a source.
|
||||
Context()->UpdatePannerSource();
|
||||
|
||||
return &aDestination;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -89,8 +89,8 @@ public:
|
|||
return mContext;
|
||||
}
|
||||
|
||||
virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
uint32_t aInput, ErrorResult& aRv);
|
||||
virtual AudioNode* Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
uint32_t aInput, ErrorResult& aRv);
|
||||
|
||||
virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
|
||||
ErrorResult& aRv);
|
||||
|
|
|
@ -33,13 +33,14 @@ public:
|
|||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
uint32_t aInput, ErrorResult& aRv) override
|
||||
virtual AudioNode* Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
uint32_t aInput, ErrorResult& aRv) override
|
||||
{
|
||||
AudioNode::Connect(aDestination, aOutput, aInput, aRv);
|
||||
AudioNode* node = AudioNode::Connect(aDestination, aOutput, aInput, aRv);
|
||||
if (!aRv.Failed()) {
|
||||
UpdateConnectedStatus();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
|
||||
|
|
|
@ -24,7 +24,7 @@ enum ChannelInterpretation {
|
|||
interface AudioNode : EventTarget {
|
||||
|
||||
[Throws]
|
||||
void connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
|
||||
AudioNode connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
|
||||
[Throws]
|
||||
void connect(AudioParam destination, optional unsigned long output = 0);
|
||||
[Throws]
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[ChromeOnly, Constructor]
|
||||
interface ChromeNodeList : NodeList {
|
||||
[Throws]
|
||||
void append(Node aNode);
|
||||
[Throws]
|
||||
void remove(Node aNode);
|
||||
};
|
|
@ -75,6 +75,7 @@ WEBIDL_FILES = [
|
|||
'ChannelSplitterNode.webidl',
|
||||
'CharacterData.webidl',
|
||||
'ChildNode.webidl',
|
||||
'ChromeNodeList.webidl',
|
||||
'ChromeNotifications.webidl',
|
||||
'ChromeUtils.webidl',
|
||||
'Client.webidl',
|
||||
|
|
|
@ -59,9 +59,9 @@ CancelChannelRunnable::Run()
|
|||
}
|
||||
|
||||
FetchEvent::FetchEvent(EventTarget* aOwner)
|
||||
: ExtendableEvent(aOwner)
|
||||
, mIsReload(false)
|
||||
, mWaitToRespond(false)
|
||||
: ExtendableEvent(aOwner)
|
||||
, mIsReload(false)
|
||||
, mWaitToRespond(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -436,15 +436,6 @@ FetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv)
|
|||
return;
|
||||
}
|
||||
|
||||
// 4.5.3.2 If the respond-with entered flag is set, then:
|
||||
// Throw an "InvalidStateError" exception.
|
||||
// Here we use |mPromise != nullptr| as respond-with enter flag
|
||||
if (mPromise) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mPromise = &aArg;
|
||||
|
||||
RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
|
||||
StopImmediatePropagation();
|
||||
mWaitToRespond = true;
|
||||
|
@ -452,6 +443,8 @@ FetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv)
|
|||
new RespondWithHandler(mChannel, mRequest->Mode(), ir->IsClientRequest(),
|
||||
ir->IsNavigationRequest(), mScriptSpec);
|
||||
aArg.AppendNativeHandler(handler);
|
||||
|
||||
WaitUntil(aArg, aRv);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(FetchEvent, ExtendableEvent)
|
||||
|
@ -460,8 +453,7 @@ NS_IMPL_RELEASE_INHERITED(FetchEvent, ExtendableEvent)
|
|||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, ExtendableEvent,
|
||||
mRequest, mPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, ExtendableEvent, mRequest)
|
||||
|
||||
ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
|
||||
: Event(aOwner, nullptr, nullptr)
|
||||
|
|
|
@ -103,7 +103,6 @@ class FetchEvent final : public ExtendableEvent
|
|||
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
|
||||
RefPtr<Request> mRequest;
|
||||
nsCString mScriptSpec;
|
||||
RefPtr<Promise> mPromise;
|
||||
bool mIsReload;
|
||||
bool mWaitToRespond;
|
||||
protected:
|
||||
|
@ -150,13 +149,6 @@ public:
|
|||
void
|
||||
RespondWith(Promise& aArg, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetPromise() const
|
||||
{
|
||||
RefPtr<Promise> p = mPromise;
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ForwardTo(const nsAString& aUrl);
|
||||
|
||||
|
|
|
@ -1175,11 +1175,11 @@ private:
|
|||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
|
||||
}
|
||||
|
||||
RefPtr<Promise> respondWithPromise = event->GetPromise();
|
||||
if (respondWithPromise) {
|
||||
RefPtr<Promise> waitUntilPromise = event->GetPromise();
|
||||
if (waitUntilPromise) {
|
||||
RefPtr<KeepAliveHandler> keepAliveHandler =
|
||||
new KeepAliveHandler(mKeepAliveToken);
|
||||
respondWithPromise->AppendNativeHandler(keepAliveHandler);
|
||||
waitUntilPromise->AppendNativeHandler(keepAliveHandler);
|
||||
}
|
||||
|
||||
// 9.8.22 If request is a non-subresource request, then: Invoke Soft Update algorithm
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 87 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 123 B |
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
var width, url, width2, url2;
|
||||
function maybeReport() {
|
||||
if (width !== undefined && url !== undefined &&
|
||||
width2 !== undefined && url2 !== undefined) {
|
||||
window.parent.postMessage({status: "result",
|
||||
width: width,
|
||||
width2: width2,
|
||||
url: url,
|
||||
url2: url2}, "*");
|
||||
}
|
||||
}
|
||||
onload = function() {
|
||||
width = document.querySelector("img").width;
|
||||
width2 = document.querySelector("img").width;
|
||||
maybeReport();
|
||||
};
|
||||
navigator.serviceWorker.onmessage = function(event) {
|
||||
if (event.data.suffix == "2") {
|
||||
url2 = event.data.url;
|
||||
} else {
|
||||
url = event.data.url;
|
||||
}
|
||||
maybeReport();
|
||||
};
|
||||
</script>
|
||||
<img src="image.png">
|
||||
<img src="image2.png">
|
|
@ -0,0 +1,41 @@
|
|||
function synthesizeImage(suffix) {
|
||||
// Serve image-20px for the first page, and image-40px for the second page.
|
||||
return clients.matchAll().then(clients => {
|
||||
var url = "image-20px.png";
|
||||
clients.forEach(client => {
|
||||
if (client.url.indexOf("?new") > 0) {
|
||||
url = "image-40px.png";
|
||||
}
|
||||
client.postMessage({suffix: suffix, url: url});
|
||||
});
|
||||
return fetch(url);
|
||||
}).then(response => {
|
||||
return response.arrayBuffer();
|
||||
}).then(ab => {
|
||||
var headers;
|
||||
if (suffix == "") {
|
||||
headers = {
|
||||
"Content-Type": "image/png",
|
||||
"Date": "Tue, 1 Jan 1990 01:02:03 GMT",
|
||||
"Cache-Control": "max-age=1",
|
||||
};
|
||||
} else {
|
||||
headers = {
|
||||
"Content-Type": "image/png",
|
||||
"Cache-Control": "no-cache",
|
||||
};
|
||||
}
|
||||
return new Response(ab, {
|
||||
status: 200,
|
||||
headers: headers,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
self.addEventListener("fetch", function(event) {
|
||||
if (event.request.url.indexOf("image.png") >= 0) {
|
||||
event.respondWith(synthesizeImage(""));
|
||||
} else if (event.request.url.indexOf("image2.png") >= 0) {
|
||||
event.respondWith(synthesizeImage("2"));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
function ok(v, msg) {
|
||||
window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
|
||||
}
|
||||
|
||||
function done(reg) {
|
||||
ok(reg.active, "The active worker should be available.");
|
||||
window.parent.postMessage({status: "registrationdone"}, "*");
|
||||
}
|
||||
|
||||
navigator.serviceWorker.ready.then(done);
|
||||
navigator.serviceWorker.register("maxage_test.js", {scope: "."});
|
||||
</script>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
navigator.serviceWorker.getRegistration(".").then(function(registration) {
|
||||
registration.unregister().then(function(success) {
|
||||
if (success) {
|
||||
window.parent.postMessage({status: "unregistrationdone"}, "*");
|
||||
}
|
||||
}, function(e) {
|
||||
dump("Unregistering the SW failed with " + e + "\n");
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -59,6 +59,12 @@ support-files =
|
|||
fetch/https/clonedresponse/register.html
|
||||
fetch/https/clonedresponse/unregister.html
|
||||
fetch/https/clonedresponse/https_test.js
|
||||
fetch/imagecache-maxage/index.html
|
||||
fetch/imagecache-maxage/image-20px.png
|
||||
fetch/imagecache-maxage/image-40px.png
|
||||
fetch/imagecache-maxage/maxage_test.js
|
||||
fetch/imagecache-maxage/register.html
|
||||
fetch/imagecache-maxage/unregister.html
|
||||
fetch/interrupt.sjs
|
||||
fetch/origin/index.sjs
|
||||
fetch/origin/index-to-https.sjs
|
||||
|
@ -282,3 +288,4 @@ skip-if = e10s # Bug 1214305
|
|||
[test_serviceworker_header.html]
|
||||
[test_openWindow.html]
|
||||
skip-if = toolkit == "android" || toolkit == "gonk" || e10s
|
||||
[test_imagecache_max_age.html]
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test that the image cache respects a synthesized image's Cache headers</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<iframe></iframe>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var iframe;
|
||||
var framesLoaded = 0;
|
||||
function runTest() {
|
||||
iframe = document.querySelector("iframe");
|
||||
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html";
|
||||
window.onmessage = function(e) {
|
||||
if (e.data.status == "ok") {
|
||||
ok(e.data.result, e.data.message);
|
||||
} else if (e.data.status == "registrationdone") {
|
||||
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html";
|
||||
} else if (e.data.status == "result") {
|
||||
switch (++framesLoaded) {
|
||||
case 1:
|
||||
is(e.data.url, "image-20px.png", "Correct url expected");
|
||||
is(e.data.url2, "image-20px.png", "Correct url expected");
|
||||
is(e.data.width, 20, "Correct width expected");
|
||||
is(e.data.width2, 20, "Correct width expected");
|
||||
// Wait for 100ms so that the image gets expired.
|
||||
setTimeout(function() {
|
||||
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html?new"
|
||||
}, 100);
|
||||
break;
|
||||
case 2:
|
||||
is(e.data.url, "image-40px.png", "Correct url expected");
|
||||
is(e.data.url2, "image-40px.png", "Correct url expected");
|
||||
// TODO: Uncomment this check when bug 1217571 gets fixed.
|
||||
// Currently because of bug 1217571, the QI in imgCacheValidator::OnStartRequest()
|
||||
// to nsICachingChannel fails, which causes the check below to fail in non-e10s.
|
||||
//is(e.data.width, 40, "Correct width expected");
|
||||
//is(e.data.width2, 40, "Correct width expected");
|
||||
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html";
|
||||
break;
|
||||
default:
|
||||
ok(false, "This should never happen");
|
||||
}
|
||||
} else if (e.data.status == "unregistrationdone") {
|
||||
window.onmessage = null;
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SimpleTest.requestFlakyTimeout("This test needs to simulate the passing of time");
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
onload = function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
["dom.serviceWorkers.interception.enabled", true],
|
||||
]}, runTest);
|
||||
};
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -116,10 +116,10 @@ function report(testName, success) {
|
|||
<script type="text/javascript"><![CDATA[
|
||||
try {
|
||||
eval("let x = 1;");
|
||||
var success = false;
|
||||
var success = true;
|
||||
}
|
||||
catch (e) { success = true; }
|
||||
is(success, true, "JS 1.7 should not work in versionless HTML script tags");
|
||||
catch (e) { success = false; }
|
||||
is(success, true, "let should work in versionless HTML script tags");
|
||||
]]></script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -1021,63 +1021,52 @@ nsresult nsHTMLEditor::InsertObject(const char* aType, nsISupports* aObject, boo
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
const char* type = aType;
|
||||
nsAutoCString type(aType);
|
||||
|
||||
// Check to see if we can insert an image file
|
||||
bool insertAsImage = false;
|
||||
nsCOMPtr<nsIURI> fileURI;
|
||||
if (0 == nsCRT::strcmp(type, kFileMime))
|
||||
nsCOMPtr<nsIFile> fileObj;
|
||||
if (type.EqualsLiteral(kFileMime))
|
||||
{
|
||||
nsCOMPtr<nsIFile> fileObj = do_QueryInterface(aObject);
|
||||
fileObj = do_QueryInterface(aObject);
|
||||
if (fileObj)
|
||||
{
|
||||
rv = NS_NewFileURI(getter_AddRefs(fileURI), fileObj);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
|
||||
NS_ENSURE_TRUE(mime, NS_ERROR_FAILURE);
|
||||
nsAutoCString contentType;
|
||||
rv = mime->GetTypeFromFile(fileObj, contentType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Accept any image type fed to us
|
||||
if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
|
||||
if (nsContentUtils::IsFileImage(fileObj, type))
|
||||
{
|
||||
insertAsImage = true;
|
||||
type = contentType.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset type.
|
||||
type.AssignLiteral(kFileMime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == nsCRT::strcmp(type, kJPEGImageMime) ||
|
||||
0 == nsCRT::strcmp(type, kJPGImageMime) ||
|
||||
0 == nsCRT::strcmp(type, kPNGImageMime) ||
|
||||
0 == nsCRT::strcmp(type, kGIFImageMime) ||
|
||||
if (type.EqualsLiteral(kJPEGImageMime) ||
|
||||
type.EqualsLiteral(kJPGImageMime) ||
|
||||
type.EqualsLiteral(kPNGImageMime) ||
|
||||
type.EqualsLiteral(kGIFImageMime) ||
|
||||
insertAsImage)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> imageStream;
|
||||
if (insertAsImage) {
|
||||
NS_ASSERTION(fileURI, "The file URI should be retrieved earlier");
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
fileURI,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = channel->Open2(getter_AddRefs(imageStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
imageStream = do_QueryInterface(aObject);
|
||||
NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
nsCString imageData;
|
||||
rv = NS_ConsumeStream(imageStream, UINT32_MAX, imageData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (insertAsImage)
|
||||
{
|
||||
rv = nsContentUtils::SlurpFileToString(fileObj, imageData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> imageStream = do_QueryInterface(aObject);
|
||||
NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE);
|
||||
|
||||
rv = imageStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = NS_ConsumeStream(imageStream, UINT32_MAX, imageData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = imageStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsAutoCString data64;
|
||||
rv = Base64Encode(imageData, data64);
|
||||
|
|
|
@ -1616,7 +1616,9 @@ public:
|
|||
* marked as needed to be recomposited.
|
||||
*/
|
||||
const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; }
|
||||
const void SetInvalidRegion(const nsIntRegion& aRect) { mInvalidRegion = aRect; }
|
||||
const void AddInvalidRegion(const nsIntRegion& aRegion) {
|
||||
mInvalidRegion.Or(mInvalidRegion, aRegion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the entirety of the layer's visible region as being invalid.
|
||||
|
|
|
@ -306,9 +306,12 @@ RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
|
|||
void
|
||||
BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
|
||||
{
|
||||
MOZ_ASSERT(mLoanedDrawTarget);
|
||||
MOZ_ASSERT(aReturned == mLoanedDrawTarget);
|
||||
mLoanedDrawTarget->SetTransform(mLoanedTransform);
|
||||
mLoanedDrawTarget = nullptr;
|
||||
if (mLoanedDrawTarget) {
|
||||
mLoanedDrawTarget->SetTransform(mLoanedTransform);
|
||||
mLoanedDrawTarget = nullptr;
|
||||
}
|
||||
aReturned = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ LayerManagerComposite::Destroy()
|
|||
RootLayer()->Destroy();
|
||||
}
|
||||
mRoot = nullptr;
|
||||
mClonedLayerTreeProperties = nullptr;
|
||||
mDestroyed = true;
|
||||
}
|
||||
}
|
||||
|
@ -175,8 +176,6 @@ LayerManagerComposite::BeginTransaction()
|
|||
}
|
||||
|
||||
mIsCompositorReady = true;
|
||||
|
||||
mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -282,42 +281,11 @@ LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
|
|||
// also to compute invalid regions properly.
|
||||
mCompositor->SetCompositionTime(aTimeStamp);
|
||||
|
||||
if (mRoot && mClonedLayerTreeProperties) {
|
||||
MOZ_ASSERT(!mTarget);
|
||||
nsIntRegion invalid =
|
||||
mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged);
|
||||
mClonedLayerTreeProperties = nullptr;
|
||||
|
||||
mInvalidRegion.Or(mInvalidRegion, invalid);
|
||||
} else if (!mTarget) {
|
||||
mInvalidRegion.Or(mInvalidRegion, mRenderBounds);
|
||||
}
|
||||
|
||||
if (mInvalidRegion.IsEmpty() && !mTarget) {
|
||||
// Composition requested, but nothing has changed. Don't do any work.
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't want our debug overlay to cause more frames to happen
|
||||
// so we will invalidate after we've decided if something changed.
|
||||
InvalidateDebugOverlay(mRenderBounds);
|
||||
|
||||
if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
|
||||
if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
|
||||
MOZ_ASSERT(!aTimeStamp.IsNull());
|
||||
// The results of our drawing always go directly into a pixel buffer,
|
||||
// so we don't need to pass any global transform here.
|
||||
mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
|
||||
|
||||
nsIntRegion opaque;
|
||||
ApplyOcclusionCulling(mRoot, opaque);
|
||||
|
||||
Render();
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
RenderToPresentationSurface();
|
||||
#endif
|
||||
mGeometryChanged = false;
|
||||
UpdateAndRender();
|
||||
} else {
|
||||
// Modified layer tree
|
||||
// Modified the layer tree.
|
||||
mGeometryChanged = true;
|
||||
}
|
||||
|
||||
|
@ -330,6 +298,70 @@ LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
LayerManagerComposite::UpdateAndRender()
|
||||
{
|
||||
nsIntRegion invalid;
|
||||
|
||||
if (mClonedLayerTreeProperties) {
|
||||
// We need to compute layer tree differences even if we're not going to
|
||||
// immediately use the resulting damage area, since ComputeDifferences
|
||||
// is also responsible for invalidates intermediate surfaces in
|
||||
// ContainerLayers.
|
||||
nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged);
|
||||
|
||||
if (mTarget) {
|
||||
// Since we're composing to an external target, we're not going to use
|
||||
// the damage region from layers changes - we want to composite
|
||||
// everything in the target bounds. Instead we accumulate the layers
|
||||
// damage region for the next window composite.
|
||||
mInvalidRegion.Or(mInvalidRegion, changed);
|
||||
} else {
|
||||
invalid = Move(changed);
|
||||
}
|
||||
}
|
||||
|
||||
if (mTarget) {
|
||||
invalid.Or(invalid, mTargetBounds);
|
||||
} else {
|
||||
// If we didn't have a previous layer tree, invalidate the entire render
|
||||
// area.
|
||||
if (!mClonedLayerTreeProperties) {
|
||||
invalid.Or(invalid, mRenderBounds);
|
||||
}
|
||||
|
||||
// Add any additional invalid rects from the window manager or previous
|
||||
// damage computed during ComposeToTarget().
|
||||
invalid.Or(invalid, mInvalidRegion);
|
||||
mInvalidRegion.SetEmpty();
|
||||
}
|
||||
|
||||
// Update cached layer tree information.
|
||||
mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
|
||||
|
||||
if (invalid.IsEmpty()) {
|
||||
// Composition requested, but nothing has changed. Don't do any work.
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't want our debug overlay to cause more frames to happen
|
||||
// so we will invalidate after we've decided if something changed.
|
||||
InvalidateDebugOverlay(mRenderBounds);
|
||||
|
||||
// The results of our drawing always go directly into a pixel buffer,
|
||||
// so we don't need to pass any global transform here.
|
||||
mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
|
||||
|
||||
nsIntRegion opaque;
|
||||
ApplyOcclusionCulling(mRoot, opaque);
|
||||
|
||||
Render(invalid);
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
RenderToPresentationSurface();
|
||||
#endif
|
||||
mGeometryChanged = false;
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize)
|
||||
{
|
||||
|
@ -641,7 +673,7 @@ LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> a
|
|||
}
|
||||
|
||||
void
|
||||
LayerManagerComposite::Render()
|
||||
LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
|
||||
{
|
||||
PROFILER_LABEL("LayerManagerComposite", "Render",
|
||||
js::ProfileEntry::Category::GRAPHICS);
|
||||
|
@ -701,8 +733,6 @@ LayerManagerComposite::Render()
|
|||
}
|
||||
}
|
||||
mCompositor->EndFrameForExternalComposition(Matrix());
|
||||
// Reset the invalid region as compositing is done
|
||||
mInvalidRegion.SetEmpty();
|
||||
mLastFrameMissedHWC = false;
|
||||
return;
|
||||
} else if (!mTarget && !haveLayerEffects) {
|
||||
|
@ -718,15 +748,6 @@ LayerManagerComposite::Render()
|
|||
}
|
||||
}
|
||||
|
||||
nsIntRegion invalid;
|
||||
if (mTarget) {
|
||||
invalid = mTargetBounds;
|
||||
} else {
|
||||
invalid = mInvalidRegion;
|
||||
// Reset the invalid region now that we've begun compositing.
|
||||
mInvalidRegion.SetEmpty();
|
||||
}
|
||||
|
||||
ParentLayerIntRect clipRect;
|
||||
Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
|
||||
Rect actualBounds;
|
||||
|
@ -736,10 +757,10 @@ LayerManagerComposite::Render()
|
|||
if (mRoot->GetClipRect()) {
|
||||
clipRect = *mRoot->GetClipRect();
|
||||
Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
|
||||
mCompositor->BeginFrame(invalid, &rect, bounds, nullptr, &actualBounds);
|
||||
mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, nullptr, &actualBounds);
|
||||
} else {
|
||||
gfx::Rect rect;
|
||||
mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds);
|
||||
mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, &rect, &actualBounds);
|
||||
clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
|
|
|
@ -297,10 +297,15 @@ private:
|
|||
nsIntRegion& aLowPrecisionScreenRegion,
|
||||
const gfx::Matrix4x4& aTransform);
|
||||
|
||||
/**
|
||||
* Update the invalid region and render it.
|
||||
*/
|
||||
void UpdateAndRender();
|
||||
|
||||
/**
|
||||
* Render the current layer tree to the active target.
|
||||
*/
|
||||
void Render();
|
||||
void Render(const nsIntRegion& aInvalidRegion);
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
void RenderToPresentationSurface();
|
||||
#endif
|
||||
|
|
|
@ -345,10 +345,13 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
|
|||
layer->SetMaskLayer(nullptr);
|
||||
}
|
||||
layer->SetAnimations(common.animations());
|
||||
layer->SetInvalidRegion(common.invalidRegion());
|
||||
layer->SetFrameMetrics(common.metrics());
|
||||
layer->SetDisplayListLog(common.displayListLog().get());
|
||||
|
||||
// The updated invalid region is added to the existing one, since we can
|
||||
// update multiple times before the next composite.
|
||||
layer->AddInvalidRegion(common.invalidRegion());
|
||||
|
||||
nsTArray<RefPtr<Layer>> maskLayers;
|
||||
for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) {
|
||||
Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer();
|
||||
|
|
|
@ -294,6 +294,7 @@ private:
|
|||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram", FPSPrintHistogram, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.acceleration.force-enabled", LayersAccelerationForceEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled", LayersAMDSwitchableGfxEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled", AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
|
||||
DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.bench.enabled", LayersBenchEnabled, bool, false);
|
||||
|
|
|
@ -1858,16 +1858,19 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma
|
|||
gfxInfo->GetAdapterVendorID(vendorID);
|
||||
gfxInfo->GetAdapterVendorID2(vendorID2);
|
||||
if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Unexpected Intel/AMD dual-GPU setup";
|
||||
return false;
|
||||
if (!gfxPrefs::LayersAMDSwitchableGfxEnabled()) {
|
||||
return false;
|
||||
}
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ID3D11Texture2D> texture;
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
desc.Width = 32;
|
||||
desc.Height = 32;
|
||||
const int texture_size = 32;
|
||||
desc.Width = texture_size;
|
||||
desc.Height = texture_size;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = format;
|
||||
|
@ -1877,7 +1880,17 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma
|
|||
desc.CPUAccessFlags = 0;
|
||||
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
||||
desc.BindFlags = bindflags;
|
||||
if (FAILED(device->CreateTexture2D(&desc, NULL, getter_AddRefs(texture)))) {
|
||||
|
||||
uint32_t color[texture_size * texture_size];
|
||||
for (size_t i = 0; i < sizeof(color)/sizeof(color[0]); i++) {
|
||||
color[i] = 0xff00ffff;
|
||||
}
|
||||
// We're going to check that sharing actually works with this format
|
||||
D3D11_SUBRESOURCE_DATA data;
|
||||
data.pSysMem = color;
|
||||
data.SysMemPitch = texture_size * 4;
|
||||
data.SysMemSlicePitch = 0;
|
||||
if (FAILED(device->CreateTexture2D(&desc, &data, getter_AddRefs(texture)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1908,6 +1921,50 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma
|
|||
return false;
|
||||
}
|
||||
|
||||
// create a staging texture for readback
|
||||
RefPtr<ID3D11Texture2D> cpuTexture;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.MiscFlags = 0;
|
||||
desc.BindFlags = 0;
|
||||
if (FAILED(device->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<IDXGIKeyedMutex> sharedMutex;
|
||||
RefPtr<ID3D11DeviceContext> deviceContext;
|
||||
sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sharedMutex));
|
||||
device->GetImmediateContext(getter_AddRefs(deviceContext));
|
||||
if (FAILED(sharedMutex->AcquireSync(0, 30*1000))) {
|
||||
gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout";
|
||||
// only wait for 30 seconds
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy to the cpu texture so that we can readback
|
||||
deviceContext->CopyResource(cpuTexture, sharedTexture);
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
int resultColor = 0;
|
||||
if (SUCCEEDED(deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
|
||||
// read the texture
|
||||
resultColor = *(int*)mapped.pData;
|
||||
deviceContext->Unmap(cpuTexture, 0);
|
||||
} else {
|
||||
gfxCriticalError() << "DoesD3D11TextureSharingWork_MapFailed";
|
||||
return false;
|
||||
}
|
||||
|
||||
sharedMutex->ReleaseSync(0);
|
||||
|
||||
// check that the color we put in is the color we get out
|
||||
if (resultColor != color[0]) {
|
||||
// Shared surfaces seem to be broken on dual AMD & Intel HW when using the
|
||||
// AMD GPU
|
||||
gfxCriticalNote << "DoesD3D11TextureSharingWork_ColorMismatch";
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<ID3D11ShaderResourceView> sharedView;
|
||||
|
||||
// This if(FAILED()) is the one that actually fails on systems affected by bug 1083071.
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_image_BMPFileHeaders_h
|
||||
#define mozilla_image_BMPFileHeaders_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace bmp {
|
||||
|
||||
// This length is stored in the |bihsize| field of bmp::FileHeader.
|
||||
struct InfoHeaderLength {
|
||||
enum {
|
||||
WIN_V2 = 12,
|
||||
WIN_V3 = 40,
|
||||
WIN_V4 = 108,
|
||||
WIN_V5 = 124,
|
||||
|
||||
// OS2_V1 is omitted; it's the same as WIN_V2.
|
||||
OS2_V2_MIN = 16, // Minimum allowed value for OS2v2.
|
||||
OS2_V2_MAX = 64, // Maximum allowed value for OS2v2.
|
||||
};
|
||||
};
|
||||
|
||||
struct FileHeader {
|
||||
char signature[2]; // String "BM".
|
||||
uint32_t filesize; // File size; unreliable in practice.
|
||||
int32_t reserved; // Zero.
|
||||
uint32_t dataoffset; // Offset to raster data.
|
||||
|
||||
// The length of the file header as defined in the BMP spec.
|
||||
static const size_t LENGTH = 14;
|
||||
};
|
||||
|
||||
struct XYZ {
|
||||
int32_t x, y, z;
|
||||
};
|
||||
|
||||
struct XYZTriple {
|
||||
XYZ r, g, b;
|
||||
};
|
||||
|
||||
struct V5InfoHeader {
|
||||
uint32_t bihsize; // Header size
|
||||
int32_t width; // Uint16 in OS/2 BMPs
|
||||
int32_t height; // Uint16 in OS/2 BMPs
|
||||
uint16_t planes; // =1
|
||||
uint16_t bpp; // Bits per pixel.
|
||||
// The rest of the header is not available in WIN_V2/OS2_V1 BMP Files
|
||||
uint32_t compression; // See Compression for valid values
|
||||
uint32_t image_size; // (compressed) image size. Can be 0 if
|
||||
// compression==0
|
||||
uint32_t xppm; // Pixels per meter, horizontal
|
||||
uint32_t yppm; // Pixels per meter, vertical
|
||||
uint32_t colors; // Used Colors
|
||||
uint32_t important_colors; // Number of important colors. 0=all
|
||||
uint32_t red_mask; // Bits used for red component
|
||||
uint32_t green_mask; // Bits used for green component
|
||||
uint32_t blue_mask; // Bits used for blue component
|
||||
uint32_t alpha_mask; // Bits used for alpha component
|
||||
uint32_t color_space; // 0x73524742=LCS_sRGB ...
|
||||
// These members are unused unless color_space == LCS_CALIBRATED_RGB
|
||||
XYZTriple white_point; // Logical white point
|
||||
uint32_t gamma_red; // Red gamma component
|
||||
uint32_t gamma_green; // Green gamma component
|
||||
uint32_t gamma_blue; // Blue gamma component
|
||||
uint32_t intent; // Rendering intent
|
||||
// These members are unused unless color_space == LCS_PROFILE_*
|
||||
uint32_t profile_offset; // Offset to profile data in bytes
|
||||
uint32_t profile_size; // Size of profile data in bytes
|
||||
uint32_t reserved; // =0
|
||||
|
||||
static const uint32_t COLOR_SPACE_LCS_SRGB = 0x73524742;
|
||||
};
|
||||
|
||||
} // namespace bmp
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_image_BMPFileHeaders_h
|
|
@ -0,0 +1,38 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_image_BMPHeaders_h
|
||||
#define mozilla_image_BMPHeaders_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace bmp {
|
||||
|
||||
// The length of the file header as defined in the BMP spec.
|
||||
static const size_t FILE_HEADER_LENGTH = 14;
|
||||
|
||||
// This lengths of the info header for the different BMP versions.
|
||||
struct InfoHeaderLength {
|
||||
enum {
|
||||
WIN_V2 = 12,
|
||||
WIN_V3 = 40,
|
||||
WIN_V4 = 108,
|
||||
WIN_V5 = 124,
|
||||
|
||||
// OS2_V1 is omitted; it's the same as WIN_V2.
|
||||
OS2_V2_MIN = 16, // Minimum allowed value for OS2v2.
|
||||
OS2_V2_MAX = 64, // Maximum allowed value for OS2v2.
|
||||
|
||||
WIN_ICO = WIN_V3,
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace bmp
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_image_BMPHeaders_h
|
|
@ -106,7 +106,7 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
|
|||
|
||||
// Allocate the buffer, which contains scanlines of the original image.
|
||||
// pad by 15 to handle overreads by the simd code
|
||||
mRowBuffer = MakeUnique<uint8_t[]>(mOriginalSize.width * sizeof(uint32_t) + 15);
|
||||
mRowBuffer.reset(new (fallible) uint8_t[mOriginalSize.width * sizeof(uint32_t) + 15]);
|
||||
if (MOZ_UNLIKELY(!mRowBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
|
|||
// can store scanlines which are already downscale because our downscaling
|
||||
// filter is separable.)
|
||||
mWindowCapacity = mYFilter->max_filter();
|
||||
mWindow = MakeUnique<uint8_t*[]>(mWindowCapacity);
|
||||
mWindow.reset(new (fallible) uint8_t*[mWindowCapacity]);
|
||||
if (MOZ_UNLIKELY(!mWindow)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
|
|||
// pad by 15 to handle overreads by the simd code
|
||||
const int rowSize = mTargetSize.width * sizeof(uint32_t) + 15;
|
||||
for (int32_t i = 0; i < mWindowCapacity; ++i) {
|
||||
mWindow[i] = new uint8_t[rowSize];
|
||||
mWindow[i] = new (fallible) uint8_t[rowSize];
|
||||
anyAllocationFailed = anyAllocationFailed || mWindow[i] == nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -273,15 +273,13 @@ private:
|
|||
, mLength(0)
|
||||
{
|
||||
MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
|
||||
mData = new (fallible) char[mCapacity];
|
||||
mData.reset(new (fallible) char[mCapacity]);
|
||||
}
|
||||
|
||||
~Chunk() { delete[] mData; }
|
||||
|
||||
Chunk(Chunk&& aOther)
|
||||
: mCapacity(aOther.mCapacity)
|
||||
, mLength(aOther.mLength)
|
||||
, mData(aOther.mData)
|
||||
, mData(Move(aOther.mData))
|
||||
{
|
||||
aOther.mCapacity = aOther.mLength = 0;
|
||||
aOther.mData = nullptr;
|
||||
|
@ -291,7 +289,7 @@ private:
|
|||
{
|
||||
mCapacity = aOther.mCapacity;
|
||||
mLength = aOther.mLength;
|
||||
mData = aOther.mData;
|
||||
mData = Move(aOther.mData);
|
||||
aOther.mCapacity = aOther.mLength = 0;
|
||||
aOther.mData = nullptr;
|
||||
return *this;
|
||||
|
@ -304,7 +302,7 @@ private:
|
|||
char* Data() const
|
||||
{
|
||||
MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
|
||||
return mData;
|
||||
return mData.get();
|
||||
}
|
||||
|
||||
void AddLength(size_t aAdditionalLength)
|
||||
|
@ -319,7 +317,7 @@ private:
|
|||
|
||||
size_t mCapacity;
|
||||
size_t mLength;
|
||||
char* mData;
|
||||
UniquePtr<char[]> mData;
|
||||
};
|
||||
|
||||
nsresult AppendChunk(Maybe<Chunk>&& aChunk);
|
||||
|
|
|
@ -575,11 +575,11 @@ nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool aNonBlocking)
|
|||
colorHeader.biSizeImage +
|
||||
maskHeader.biSizeImage;
|
||||
|
||||
char* buffer = new char[iconSize];
|
||||
UniquePtr<char[]> buffer = MakeUnique<char[]>(iconSize);
|
||||
if (!buffer) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
} else {
|
||||
char* whereTo = buffer;
|
||||
char* whereTo = buffer.get();
|
||||
int howMuch;
|
||||
|
||||
// the data starts with an icon file header
|
||||
|
@ -640,7 +640,7 @@ nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool aNonBlocking)
|
|||
iconSize, iconSize, aNonBlocking);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
uint32_t written;
|
||||
rv = outStream->Write(buffer, iconSize, &written);
|
||||
rv = outStream->Write(buffer.get(), iconSize, &written);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
NS_ADDREF(*_retval = inStream);
|
||||
}
|
||||
|
@ -650,7 +650,6 @@ nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool aNonBlocking)
|
|||
delete maskInfo;
|
||||
} // if we got mask bits
|
||||
delete colorInfo;
|
||||
delete [] buffer;
|
||||
} // if we allocated the buffer
|
||||
} // if we got mask size
|
||||
|
||||
|
|
|
@ -25,28 +25,28 @@
|
|||
//
|
||||
// WinBMPv2.
|
||||
// - First is a 14 byte file header that includes: the magic number ("BM"),
|
||||
// file size, and offset to the pixel data (|dataoffset|).
|
||||
// file size, and offset to the pixel data (|mDataOffset|).
|
||||
// - Next is a 12 byte info header which includes: the info header size
|
||||
// (bihsize), width, height, number of color planes, and bits-per-pixel
|
||||
// (|bpp|) which must be 1, 4, 8 or 24.
|
||||
// - Next is the semi-optional color table, which has length 2^|bpp| and has 3
|
||||
// bytes per value (BGR). The color table is required if |bpp| is 1, 4, or 8.
|
||||
// (mBIHSize), width, height, number of color planes, and bits-per-pixel
|
||||
// (|mBpp|) which must be 1, 4, 8 or 24.
|
||||
// - Next is the semi-optional color table, which has length 2^|mBpp| and has 3
|
||||
// bytes per value (BGR). The color table is required if |mBpp| is 1, 4, or 8.
|
||||
// - Next is an optional gap.
|
||||
// - Next is the pixel data, which is pointed to by |dataoffset|.
|
||||
// - Next is the pixel data, which is pointed to by |mDataOffset|.
|
||||
//
|
||||
// WinBMPv3. This is the most widely used version.
|
||||
// - It changed the info header to 40 bytes by taking the WinBMPv2 info
|
||||
// header, enlargening its width and height fields, and adding more fields
|
||||
// including: a compression type (|compression|) and number of colors
|
||||
// (|colors|).
|
||||
// including: a compression type (|mCompression|) and number of colors
|
||||
// (|mNumColors|).
|
||||
// - The semi-optional color table is now 4 bytes per value (BGR0), and its
|
||||
// length is |colors|, or 2^|bpp| if |colors| is zero.
|
||||
// - |compression| can be RGB (i.e. no compression), RLE4 (if bpp==4) or RLE8
|
||||
// (if bpp==8) values.
|
||||
// length is |mNumColors|, or 2^|mBpp| if |mNumColors| is zero.
|
||||
// - |mCompression| can be RGB (i.e. no compression), RLE4 (if |mBpp|==4) or
|
||||
// RLE8 (if |mBpp|==8) values.
|
||||
//
|
||||
// WinBMPv3-NT. A variant of WinBMPv3.
|
||||
// - It did not change the info header layout from WinBMPv3.
|
||||
// - |bpp| can now be 16 or 32, in which case |compression| can be RGB or the
|
||||
// - |mBpp| can now be 16 or 32, in which case |mCompression| can be RGB or the
|
||||
// new BITFIELDS value; in the latter case an additional 12 bytes of color
|
||||
// bitfields follow the info header.
|
||||
//
|
||||
|
@ -137,7 +137,8 @@ SetPixel(uint32_t*& aDecoded, uint8_t aRed, uint8_t aGreen,
|
|||
}
|
||||
|
||||
static void
|
||||
SetPixel(uint32_t*& aDecoded, uint8_t idx, bmp::ColorTableEntry* aColors)
|
||||
SetPixel(uint32_t*& aDecoded, uint8_t idx,
|
||||
const UniquePtr<ColorTableEntry[]>& aColors)
|
||||
{
|
||||
SetPixel(aDecoded,
|
||||
aColors[idx].mRed, aColors[idx].mGreen, aColors[idx].mBlue);
|
||||
|
@ -150,7 +151,7 @@ SetPixel(uint32_t*& aDecoded, uint8_t idx, bmp::ColorTableEntry* aColors)
|
|||
/// @param aCount Current count. Is decremented by one or two.
|
||||
static void
|
||||
Set4BitPixel(uint32_t*& aDecoded, uint8_t aData, uint32_t& aCount,
|
||||
bmp::ColorTableEntry* aColors)
|
||||
const UniquePtr<ColorTableEntry[]>& aColors)
|
||||
{
|
||||
uint8_t idx = aData >> 4;
|
||||
SetPixel(aDecoded, idx, aColors);
|
||||
|
@ -171,9 +172,12 @@ GetBMPLog()
|
|||
return sBMPLog;
|
||||
}
|
||||
|
||||
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
|
||||
// The length of the mBIHSize field in the info header.
|
||||
static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
|
||||
|
||||
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength)
|
||||
: Decoder(aImage)
|
||||
, mLexer(Transition::To(State::FILE_HEADER, FileHeader::LENGTH))
|
||||
, mLexer(Transition::To(aState, aLength))
|
||||
, mIsWithinICO(false)
|
||||
, mMayHaveTransparency(false)
|
||||
, mDoesHaveTransparency(false)
|
||||
|
@ -185,53 +189,43 @@ nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
|
|||
, mCurrentPos(0)
|
||||
, mAbsoluteModeNumPixels(0)
|
||||
{
|
||||
memset(&mBFH, 0, sizeof(mBFH));
|
||||
memset(&mBIH, 0, sizeof(mBIH));
|
||||
}
|
||||
|
||||
// Constructor for normal BMP files.
|
||||
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
|
||||
: nsBMPDecoder(aImage, State::FILE_HEADER, FILE_HEADER_LENGTH)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor used for WinBMPv3-ICO files, which lack a file header.
|
||||
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset)
|
||||
: nsBMPDecoder(aImage, State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH)
|
||||
{
|
||||
SetIsWithinICO();
|
||||
|
||||
// Even though the file header isn't present in this case, the dataOffset
|
||||
// field is set as if it is, and so we must increment mPreGapLength
|
||||
// accordingly.
|
||||
mPreGapLength += FILE_HEADER_LENGTH;
|
||||
|
||||
// This is the one piece of data we normally get from a BMP file header, so
|
||||
// it must be provided via an argument.
|
||||
mH.mDataOffset = aDataOffset;
|
||||
}
|
||||
|
||||
nsBMPDecoder::~nsBMPDecoder()
|
||||
{
|
||||
delete[] mColors;
|
||||
}
|
||||
|
||||
// Obtains the bits per pixel from the internal BIH header.
|
||||
int32_t
|
||||
nsBMPDecoder::GetBitsPerPixel() const
|
||||
{
|
||||
return mBIH.bpp;
|
||||
}
|
||||
|
||||
// Obtains the width from the internal BIH header.
|
||||
int32_t
|
||||
nsBMPDecoder::GetWidth() const
|
||||
{
|
||||
return mBIH.width;
|
||||
}
|
||||
|
||||
// Obtains the absolute value of the height from the internal BIH header.
|
||||
// If it's positive the bitmap is stored bottom to top, otherwise top to bottom.
|
||||
int32_t
|
||||
nsBMPDecoder::GetHeight() const
|
||||
{
|
||||
return abs(mBIH.height);
|
||||
}
|
||||
|
||||
// Obtains the internal output image buffer.
|
||||
uint32_t*
|
||||
nsBMPDecoder::GetImageData()
|
||||
{
|
||||
return reinterpret_cast<uint32_t*>(mImageData);
|
||||
}
|
||||
|
||||
// Obtains the size of the compressed image resource.
|
||||
int32_t
|
||||
nsBMPDecoder::GetCompressedImageSize() const
|
||||
{
|
||||
// In the RGB case image_size might not be set, so compute it manually.
|
||||
// In the RGB case mImageSize might not be set, so compute it manually.
|
||||
MOZ_ASSERT(mPixelRowSize != 0);
|
||||
return mBIH.compression == Compression::RGB
|
||||
? mPixelRowSize * GetHeight()
|
||||
: mBIH.image_size;
|
||||
return mH.mCompression == Compression::RGB
|
||||
? mPixelRowSize * AbsoluteHeight()
|
||||
: mH.mImageSize;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -247,7 +241,7 @@ nsBMPDecoder::FinishInternal()
|
|||
if (!IsMetadataDecode() && HasSize()) {
|
||||
|
||||
// Invalidate.
|
||||
nsIntRect r(0, 0, mBIH.width, GetHeight());
|
||||
nsIntRect r(0, 0, mH.mWidth, AbsoluteHeight());
|
||||
PostInvalidation(r);
|
||||
|
||||
if (mDoesHaveTransparency) {
|
||||
|
@ -400,11 +394,11 @@ nsBMPDecoder::RowBuffer()
|
|||
return reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer()) + mCurrentPos;
|
||||
}
|
||||
|
||||
// Convert from row (1..height) to absolute line (0..height-1).
|
||||
int32_t line = (mBIH.height < 0)
|
||||
? -mBIH.height - mCurrentRow
|
||||
// Convert from row (1..mHeight) to absolute line (0..mHeight-1).
|
||||
int32_t line = (mH.mHeight < 0)
|
||||
? -mH.mHeight - mCurrentRow
|
||||
: mCurrentRow - 1;
|
||||
int32_t offset = line * mBIH.width + mCurrentPos;
|
||||
int32_t offset = line * mH.mWidth + mCurrentPos;
|
||||
return reinterpret_cast<uint32_t*>(mImageData) + offset;
|
||||
}
|
||||
|
||||
|
@ -420,7 +414,7 @@ nsBMPDecoder::FinishRow()
|
|||
Some(invalidRect.mTargetSizeRect));
|
||||
}
|
||||
} else {
|
||||
PostInvalidation(IntRect(0, mCurrentRow, mBIH.width, 1));
|
||||
PostInvalidation(IntRect(0, mCurrentRow, mH.mWidth, 1));
|
||||
}
|
||||
mCurrentRow--;
|
||||
}
|
||||
|
@ -466,59 +460,49 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||
return;
|
||||
}
|
||||
|
||||
// The length of the bihsize field in the info header.
|
||||
static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
|
||||
|
||||
LexerTransition<nsBMPDecoder::State>
|
||||
nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
|
||||
{
|
||||
mPreGapLength += aLength;
|
||||
|
||||
mBFH.signature[0] = aData[0];
|
||||
mBFH.signature[1] = aData[1];
|
||||
bool signatureOk = mBFH.signature[0] == 'B' && mBFH.signature[1] == 'M';
|
||||
bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
|
||||
if (!signatureOk) {
|
||||
PostDataError();
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
|
||||
// Nb: this field is unreliable. In Windows BMPs it's the file size, but in
|
||||
// OS/2 BMPs it's sometimes the size of the file and info headers. It doesn't
|
||||
// matter because we don't consult it.
|
||||
mBFH.filesize = LittleEndian::readUint32(aData + 2);
|
||||
// We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
|
||||
|
||||
mBFH.reserved = 0;
|
||||
|
||||
mBFH.dataoffset = LittleEndian::readUint32(aData + 10);
|
||||
mH.mDataOffset = LittleEndian::readUint32(aData + 10);
|
||||
|
||||
return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
|
||||
}
|
||||
|
||||
// We read the info header in two steps: (a) read the bihsize field to
|
||||
// We read the info header in two steps: (a) read the mBIHSize field to
|
||||
// determine how long the header is; (b) read the rest of the header.
|
||||
LexerTransition<nsBMPDecoder::State>
|
||||
nsBMPDecoder::ReadInfoHeaderSize(const char* aData, size_t aLength)
|
||||
{
|
||||
mPreGapLength += aLength;
|
||||
|
||||
mBIH.bihsize = LittleEndian::readUint32(aData);
|
||||
mH.mBIHSize = LittleEndian::readUint32(aData);
|
||||
|
||||
bool bihsizeOk = mBIH.bihsize == InfoHeaderLength::WIN_V2 ||
|
||||
mBIH.bihsize == InfoHeaderLength::WIN_V3 ||
|
||||
mBIH.bihsize == InfoHeaderLength::WIN_V4 ||
|
||||
mBIH.bihsize == InfoHeaderLength::WIN_V5 ||
|
||||
(mBIH.bihsize >= InfoHeaderLength::OS2_V2_MIN &&
|
||||
mBIH.bihsize <= InfoHeaderLength::OS2_V2_MAX);
|
||||
if (!bihsizeOk) {
|
||||
bool bihSizeOk = mH.mBIHSize == InfoHeaderLength::WIN_V2 ||
|
||||
mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
|
||||
mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
|
||||
mH.mBIHSize == InfoHeaderLength::WIN_V5 ||
|
||||
(mH.mBIHSize >= InfoHeaderLength::OS2_V2_MIN &&
|
||||
mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
|
||||
if (!bihSizeOk) {
|
||||
PostDataError();
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
// ICO BMPs must have a WinVMPv3 header. nsICODecoder should have already
|
||||
// terminated decoding if this isn't the case.
|
||||
MOZ_ASSERT_IF(mIsWithinICO, mBIH.bihsize == InfoHeaderLength::WIN_V3);
|
||||
MOZ_ASSERT_IF(mIsWithinICO, mH.mBIHSize == InfoHeaderLength::WIN_V3);
|
||||
|
||||
return Transition::To(State::INFO_HEADER_REST,
|
||||
mBIH.bihsize - BIHSIZE_FIELD_LENGTH);
|
||||
mH.mBIHSize - BIHSIZE_FIELD_LENGTH);
|
||||
}
|
||||
|
||||
LexerTransition<nsBMPDecoder::State>
|
||||
|
@ -526,28 +510,26 @@ nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
|
|||
{
|
||||
mPreGapLength += aLength;
|
||||
|
||||
// |width| and |height| may be signed (Windows) or unsigned (OS/2). We just
|
||||
// |mWidth| and |mHeight| may be signed (Windows) or unsigned (OS/2). We just
|
||||
// read as unsigned because in practice that's good enough.
|
||||
if (mBIH.bihsize == InfoHeaderLength::WIN_V2) {
|
||||
mBIH.width = LittleEndian::readUint16(aData + 0);
|
||||
mBIH.height = LittleEndian::readUint16(aData + 2);
|
||||
mBIH.planes = LittleEndian::readUint16(aData + 4);
|
||||
mBIH.bpp = LittleEndian::readUint16(aData + 6);
|
||||
if (mH.mBIHSize == InfoHeaderLength::WIN_V2) {
|
||||
mH.mWidth = LittleEndian::readUint16(aData + 0);
|
||||
mH.mHeight = LittleEndian::readUint16(aData + 2);
|
||||
// We ignore the planes (aData + 4) field; it should always be 1.
|
||||
mH.mBpp = LittleEndian::readUint16(aData + 6);
|
||||
} else {
|
||||
mBIH.width = LittleEndian::readUint32(aData + 0);
|
||||
mBIH.height = LittleEndian::readUint32(aData + 4);
|
||||
mBIH.planes = LittleEndian::readUint16(aData + 8);
|
||||
mBIH.bpp = LittleEndian::readUint16(aData + 10);
|
||||
mH.mWidth = LittleEndian::readUint32(aData + 0);
|
||||
mH.mHeight = LittleEndian::readUint32(aData + 4);
|
||||
// We ignore the planes (aData + 4) field; it should always be 1.
|
||||
mH.mBpp = LittleEndian::readUint16(aData + 10);
|
||||
|
||||
// For OS2-BMPv2 the info header may be as little as 16 bytes, so be
|
||||
// careful for these fields.
|
||||
mBIH.compression = aLength >= 16 ? LittleEndian::readUint32(aData + 12) : 0;
|
||||
mBIH.image_size = aLength >= 20 ? LittleEndian::readUint32(aData + 16) : 0;
|
||||
mBIH.xppm = aLength >= 24 ? LittleEndian::readUint32(aData + 20) : 0;
|
||||
mBIH.yppm = aLength >= 28 ? LittleEndian::readUint32(aData + 24) : 0;
|
||||
mBIH.colors = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
|
||||
mBIH.important_colors
|
||||
= aLength >= 36 ? LittleEndian::readUint32(aData + 32) : 0;
|
||||
mH.mCompression = aLength >= 16 ? LittleEndian::readUint32(aData + 12) : 0;
|
||||
mH.mImageSize = aLength >= 20 ? LittleEndian::readUint32(aData + 16) : 0;
|
||||
// We ignore the xppm (aData + 20) and yppm (aData + 24) fields.
|
||||
mH.mNumColors = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
|
||||
// We ignore the important_colors (aData + 36) field.
|
||||
|
||||
// For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
|
||||
// fields in the info header which we ignore, with the possible exception
|
||||
|
@ -557,52 +539,52 @@ nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
|
|||
// Run with NSPR_LOG_MODULES=BMPDecoder:4 set to see this output.
|
||||
MOZ_LOG(GetBMPLog(), LogLevel::Debug,
|
||||
("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u\n",
|
||||
mBIH.bihsize, mBIH.width, mBIH.height, uint32_t(mBIH.bpp),
|
||||
mBIH.compression, mBIH.colors));
|
||||
mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
|
||||
mH.mCompression, mH.mNumColors));
|
||||
|
||||
// BMPs with negative width are invalid. Also, reject extremely wide images
|
||||
// to keep the math sane. And reject INT_MIN as a height because you can't
|
||||
// get its absolute value (because -INT_MIN is one more than INT_MAX).
|
||||
const int32_t k64KWidth = 0x0000FFFF;
|
||||
bool sizeOk = 0 <= mBIH.width && mBIH.width <= k64KWidth &&
|
||||
mBIH.height != INT_MIN;
|
||||
bool sizeOk = 0 <= mH.mWidth && mH.mWidth <= k64KWidth &&
|
||||
mH.mHeight != INT_MIN;
|
||||
if (!sizeOk) {
|
||||
PostDataError();
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
|
||||
// Check bpp and compression.
|
||||
// Check mBpp and mCompression.
|
||||
bool bppCompressionOk =
|
||||
(mBIH.compression == Compression::RGB &&
|
||||
(mBIH.bpp == 1 || mBIH.bpp == 4 || mBIH.bpp == 8 ||
|
||||
mBIH.bpp == 16 || mBIH.bpp == 24 || mBIH.bpp == 32)) ||
|
||||
(mBIH.compression == Compression::RLE8 && mBIH.bpp == 8) ||
|
||||
(mBIH.compression == Compression::RLE4 && mBIH.bpp == 4) ||
|
||||
(mBIH.compression == Compression::BITFIELDS &&
|
||||
(mBIH.bpp == 16 || mBIH.bpp == 32));
|
||||
(mH.mCompression == Compression::RGB &&
|
||||
(mH.mBpp == 1 || mH.mBpp == 4 || mH.mBpp == 8 ||
|
||||
mH.mBpp == 16 || mH.mBpp == 24 || mH.mBpp == 32)) ||
|
||||
(mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
|
||||
(mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
|
||||
(mH.mCompression == Compression::BITFIELDS &&
|
||||
(mH.mBpp == 16 || mH.mBpp == 32));
|
||||
if (!bppCompressionOk) {
|
||||
PostDataError();
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
|
||||
// Post our size to the superclass.
|
||||
uint32_t realHeight = GetHeight();
|
||||
PostSize(mBIH.width, realHeight);
|
||||
mCurrentRow = realHeight;
|
||||
uint32_t absHeight = AbsoluteHeight();
|
||||
PostSize(mH.mWidth, absHeight);
|
||||
mCurrentRow = absHeight;
|
||||
|
||||
// Round it up to the nearest byte count, then pad to 4-byte boundary.
|
||||
// Compute this even for a metadate decode because GetCompressedImageSize()
|
||||
// relies on it.
|
||||
mPixelRowSize = (mBIH.bpp * mBIH.width + 7) / 8;
|
||||
mPixelRowSize = (mH.mBpp * mH.mWidth + 7) / 8;
|
||||
uint32_t surplus = mPixelRowSize % 4;
|
||||
if (surplus != 0) {
|
||||
mPixelRowSize += 4 - surplus;
|
||||
}
|
||||
|
||||
size_t bitFieldsLengthStillToRead = 0;
|
||||
if (mBIH.compression == Compression::BITFIELDS) {
|
||||
if (mH.mCompression == Compression::BITFIELDS) {
|
||||
// Need to read bitfields.
|
||||
if (mBIH.bihsize >= InfoHeaderLength::WIN_V4) {
|
||||
if (mH.mBIHSize >= InfoHeaderLength::WIN_V4) {
|
||||
// Bitfields are present in the info header, so we can read them
|
||||
// immediately.
|
||||
mBitFields.ReadFromHeader(aData + 36, /* aReadAlpha = */ true);
|
||||
|
@ -611,10 +593,10 @@ nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
|
|||
// ReadBitfields().
|
||||
bitFieldsLengthStillToRead = BitFields::LENGTH;
|
||||
}
|
||||
} else if (mBIH.bpp == 16) {
|
||||
} else if (mH.mBpp == 16) {
|
||||
// No bitfields specified; use the default 5-5-5 values.
|
||||
mBitFields.SetR5G5B5();
|
||||
} else if (mBIH.bpp == 32) {
|
||||
} else if (mH.mBpp == 32) {
|
||||
// No bitfields specified; use the default 8-8-8 values.
|
||||
mBitFields.SetR8G8B8();
|
||||
}
|
||||
|
@ -647,10 +629,10 @@ nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
|
|||
// Note that RLE-encoded BMPs might be transparent because the 'delta' mode
|
||||
// can skip pixels and cause implicit transparency.
|
||||
mMayHaveTransparency =
|
||||
(mBIH.compression == Compression::RGB && mIsWithinICO && mBIH.bpp == 32) ||
|
||||
mBIH.compression == Compression::RLE8 ||
|
||||
mBIH.compression == Compression::RLE4 ||
|
||||
(mBIH.compression == Compression::BITFIELDS &&
|
||||
(mH.mCompression == Compression::RGB && mIsWithinICO && mH.mBpp == 32) ||
|
||||
mH.mCompression == Compression::RLE8 ||
|
||||
mH.mCompression == Compression::RLE4 ||
|
||||
(mH.mCompression == Compression::BITFIELDS &&
|
||||
mBitFields.mAlpha.IsPresent());
|
||||
if (mMayHaveTransparency) {
|
||||
PostHasTransparency();
|
||||
|
@ -663,19 +645,19 @@ nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
|
|||
}
|
||||
|
||||
// Set up the color table, if present; it'll be filled in by ReadColorTable().
|
||||
if (mBIH.bpp <= 8) {
|
||||
mNumColors = 1 << mBIH.bpp;
|
||||
if (0 < mBIH.colors && mBIH.colors < mNumColors) {
|
||||
mNumColors = mBIH.colors;
|
||||
if (mH.mBpp <= 8) {
|
||||
mNumColors = 1 << mH.mBpp;
|
||||
if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
|
||||
mNumColors = mH.mNumColors;
|
||||
}
|
||||
|
||||
// Always allocate and zero 256 entries, even though mNumColors might be
|
||||
// smaller, because the file might erroneously index past mNumColors.
|
||||
mColors = new ColorTableEntry[256];
|
||||
memset(mColors, 0, 256 * sizeof(ColorTableEntry));
|
||||
mColors = MakeUnique<ColorTableEntry[]>(256);
|
||||
memset(mColors.get(), 0, 256 * sizeof(ColorTableEntry));
|
||||
|
||||
// OS/2 Bitmaps have no padding byte.
|
||||
mBytesPerColor = (mBIH.bihsize == InfoHeaderLength::WIN_V2) ? 3 : 4;
|
||||
mBytesPerColor = (mH.mBIHSize == InfoHeaderLength::WIN_V2) ? 3 : 4;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
|
@ -718,25 +700,25 @@ nsBMPDecoder::ReadColorTable(const char* aData, size_t aLength)
|
|||
}
|
||||
|
||||
// We know how many bytes we've read so far (mPreGapLength) and we know the
|
||||
// offset of the pixel data (mBFH.dataoffset), so we can determine the length
|
||||
// offset of the pixel data (mH.mDataOffset), so we can determine the length
|
||||
// of the gap (possibly zero) between the color table and the pixel data.
|
||||
//
|
||||
// If the gap is negative the file must be malformed (e.g. mBFH.dataoffset
|
||||
// If the gap is negative the file must be malformed (e.g. mH.mDataOffset
|
||||
// points into the middle of the color palette instead of past the end) and
|
||||
// we give up.
|
||||
if (mPreGapLength > mBFH.dataoffset) {
|
||||
if (mPreGapLength > mH.mDataOffset) {
|
||||
PostDataError();
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
uint32_t gapLength = mBFH.dataoffset - mPreGapLength;
|
||||
uint32_t gapLength = mH.mDataOffset - mPreGapLength;
|
||||
return Transition::To(State::GAP, gapLength);
|
||||
}
|
||||
|
||||
LexerTransition<nsBMPDecoder::State>
|
||||
nsBMPDecoder::SkipGap()
|
||||
{
|
||||
bool hasRLE = mBIH.compression == Compression::RLE8 ||
|
||||
mBIH.compression == Compression::RLE4;
|
||||
bool hasRLE = mH.mCompression == Compression::RLE8 ||
|
||||
mH.mCompression == Compression::RLE4;
|
||||
return hasRLE
|
||||
? Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH)
|
||||
: Transition::To(State::PIXEL_ROW, mPixelRowSize);
|
||||
|
@ -749,8 +731,8 @@ nsBMPDecoder::ReadPixelRow(const char* aData)
|
|||
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(aData);
|
||||
uint32_t* dst = RowBuffer();
|
||||
uint32_t lpos = mBIH.width;
|
||||
switch (mBIH.bpp) {
|
||||
uint32_t lpos = mH.mWidth;
|
||||
switch (mH.mBpp) {
|
||||
case 1:
|
||||
while (lpos > 0) {
|
||||
int8_t bit;
|
||||
|
@ -817,8 +799,8 @@ nsBMPDecoder::ReadPixelRow(const char* aData)
|
|||
break;
|
||||
|
||||
case 32:
|
||||
if (mBIH.compression == Compression::RGB && mIsWithinICO &&
|
||||
mBIH.bpp == 32) {
|
||||
if (mH.mCompression == Compression::RGB && mIsWithinICO &&
|
||||
mH.mBpp == 32) {
|
||||
// This is a special case only used for 32bpp WinBMPv3-ICO files, which
|
||||
// could be in either 0RGB or ARGB format.
|
||||
while (lpos > 0) {
|
||||
|
@ -894,11 +876,11 @@ nsBMPDecoder::ReadRLESegment(const char* aData)
|
|||
//
|
||||
// Work around bitmaps that specify too many pixels.
|
||||
uint32_t pixelsNeeded =
|
||||
std::min<uint32_t>(mBIH.width - mCurrentPos, byte1);
|
||||
std::min<uint32_t>(mH.mWidth - mCurrentPos, byte1);
|
||||
if (pixelsNeeded) {
|
||||
uint32_t* dst = RowBuffer();
|
||||
mCurrentPos += pixelsNeeded;
|
||||
if (mBIH.compression == Compression::RLE8) {
|
||||
if (mH.mCompression == Compression::RLE8) {
|
||||
do {
|
||||
SetPixel(dst, byte2, mColors);
|
||||
pixelsNeeded --;
|
||||
|
@ -934,7 +916,7 @@ nsBMPDecoder::ReadRLESegment(const char* aData)
|
|||
MOZ_ASSERT(mAbsoluteModeNumPixels == 0);
|
||||
mAbsoluteModeNumPixels = byte2;
|
||||
uint32_t length = byte2;
|
||||
if (mBIH.compression == Compression::RLE4) {
|
||||
if (mH.mCompression == Compression::RLE4) {
|
||||
length = (length + 1) / 2; // halve, rounding up
|
||||
}
|
||||
if (length & 1) {
|
||||
|
@ -959,8 +941,8 @@ nsBMPDecoder::ReadRLEDelta(const char* aData)
|
|||
|
||||
// Handle the XDelta.
|
||||
mCurrentPos += uint8_t(aData[0]);
|
||||
if (mCurrentPos > mBIH.width) {
|
||||
mCurrentPos = mBIH.width;
|
||||
if (mCurrentPos > mH.mWidth) {
|
||||
mCurrentPos = mH.mWidth;
|
||||
}
|
||||
|
||||
// Handle the Y Delta.
|
||||
|
@ -989,7 +971,7 @@ nsBMPDecoder::ReadRLEAbsolute(const char* aData, size_t aLength)
|
|||
uint32_t n = mAbsoluteModeNumPixels;
|
||||
mAbsoluteModeNumPixels = 0;
|
||||
|
||||
if (mCurrentPos + n > uint32_t(mBIH.width)) {
|
||||
if (mCurrentPos + n > uint32_t(mH.mWidth)) {
|
||||
// Bad data. Stop decoding; at least part of the image may have been
|
||||
// decoded.
|
||||
return Transition::Terminate(State::SUCCESS);
|
||||
|
@ -1000,7 +982,7 @@ nsBMPDecoder::ReadRLEAbsolute(const char* aData, size_t aLength)
|
|||
uint32_t* dst = RowBuffer();
|
||||
uint32_t iSrc = 0;
|
||||
uint32_t* oldPos = dst;
|
||||
if (mBIH.compression == Compression::RLE8) {
|
||||
if (mH.mCompression == Compression::RLE8) {
|
||||
while (n > 0) {
|
||||
SetPixel(dst, aData[iSrc], mColors);
|
||||
n--;
|
||||
|
|
|
@ -7,16 +7,43 @@
|
|||
#ifndef mozilla_image_decoders_nsBMPDecoder_h
|
||||
#define mozilla_image_decoders_nsBMPDecoder_h
|
||||
|
||||
#include "BMPFileHeaders.h"
|
||||
#include "BMPHeaders.h"
|
||||
#include "Decoder.h"
|
||||
#include "gfxColor.h"
|
||||
#include "StreamingLexer.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
namespace bmp {
|
||||
|
||||
/// This struct contains the fields from the file header and info header that
|
||||
/// we use during decoding. (Excluding bitfields fields, which are kept in
|
||||
/// BitFields.)
|
||||
struct Header {
|
||||
uint32_t mDataOffset; // Offset to raster data.
|
||||
uint32_t mBIHSize; // Header size.
|
||||
int32_t mWidth; // Image width.
|
||||
int32_t mHeight; // Image height.
|
||||
uint16_t mBpp; // Bits per pixel.
|
||||
uint32_t mCompression; // See struct Compression for valid values.
|
||||
uint32_t mImageSize; // (compressed) image size. Can be 0 if
|
||||
// mCompression==0.
|
||||
uint32_t mNumColors; // Used colors.
|
||||
|
||||
Header()
|
||||
: mDataOffset(0)
|
||||
, mBIHSize(0)
|
||||
, mWidth(0)
|
||||
, mHeight(0)
|
||||
, mBpp(0)
|
||||
, mCompression(0)
|
||||
, mImageSize(0)
|
||||
, mNumColors(0)
|
||||
{}
|
||||
};
|
||||
|
||||
/// An entry in the color table.
|
||||
struct ColorTableEntry {
|
||||
uint8_t mRed;
|
||||
|
@ -98,23 +125,17 @@ class nsBMPDecoder : public Decoder
|
|||
public:
|
||||
~nsBMPDecoder();
|
||||
|
||||
/// Obtains the bits per pixel from the internal BIH header.
|
||||
int32_t GetBitsPerPixel() const;
|
||||
|
||||
/// Obtains the width from the internal BIH header.
|
||||
int32_t GetWidth() const;
|
||||
|
||||
/// Obtains the abs-value of the height from the internal BIH header.
|
||||
int32_t GetHeight() const;
|
||||
|
||||
/// Obtains the internal output image buffer.
|
||||
uint32_t* GetImageData();
|
||||
uint32_t* GetImageData() { return reinterpret_cast<uint32_t*>(mImageData); }
|
||||
|
||||
/// Obtains the length of the internal output image buffer.
|
||||
size_t GetImageDataLength() const { return mImageDataLength; }
|
||||
|
||||
/// Obtains the size of the compressed image resource.
|
||||
int32_t GetCompressedImageSize() const;
|
||||
|
||||
/// Mark this BMP as being within an ICO file.
|
||||
/// Mark this BMP as being within an ICO file. Only used for testing purposes
|
||||
/// because the ICO-specific constructor does this marking automatically.
|
||||
void SetIsWithinICO() { mIsWithinICO = true; }
|
||||
|
||||
/// Did the BMP file have alpha data of any kind? (Only use this after the
|
||||
|
@ -136,14 +157,6 @@ private:
|
|||
friend class DecoderFactory;
|
||||
friend class nsICODecoder;
|
||||
|
||||
// Decoders should only be instantiated via DecoderFactory.
|
||||
// XXX(seth): nsICODecoder is temporarily an exception to this rule.
|
||||
explicit nsBMPDecoder(RasterImage* aImage);
|
||||
|
||||
uint32_t* RowBuffer();
|
||||
|
||||
void FinishRow();
|
||||
|
||||
enum class State {
|
||||
FILE_HEADER,
|
||||
INFO_HEADER_SIZE,
|
||||
|
@ -159,6 +172,23 @@ private:
|
|||
FAILURE
|
||||
};
|
||||
|
||||
// This is the constructor used by DecoderFactory.
|
||||
explicit nsBMPDecoder(RasterImage* aImage);
|
||||
|
||||
// This is the constructor used by nsICODecoder.
|
||||
// XXX(seth): nsICODecoder is temporarily an exception to the rule that
|
||||
// decoders should only be instantiated via DecoderFactory.
|
||||
nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset);
|
||||
|
||||
// Helper constructor called by the other two.
|
||||
nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength);
|
||||
|
||||
int32_t AbsoluteHeight() const { return abs(mH.mHeight); }
|
||||
|
||||
uint32_t* RowBuffer();
|
||||
|
||||
void FinishRow();
|
||||
|
||||
LexerTransition<State> ReadFileHeader(const char* aData, size_t aLength);
|
||||
LexerTransition<State> ReadInfoHeaderSize(const char* aData, size_t aLength);
|
||||
LexerTransition<State> ReadInfoHeaderRest(const char* aData, size_t aLength);
|
||||
|
@ -172,8 +202,7 @@ private:
|
|||
|
||||
StreamingLexer<State> mLexer;
|
||||
|
||||
bmp::FileHeader mBFH;
|
||||
bmp::V5InfoHeader mBIH;
|
||||
bmp::Header mH;
|
||||
|
||||
// If the BMP is within an ICO file our treatment of it differs slightly.
|
||||
bool mIsWithinICO;
|
||||
|
@ -190,7 +219,7 @@ private:
|
|||
|
||||
uint32_t mNumColors; // The number of used colors, i.e. the number of
|
||||
// entries in mColors, if it's present.
|
||||
bmp::ColorTableEntry* mColors; // The color table, if it's present.
|
||||
UniquePtr<bmp::ColorTableEntry[]> mColors; // The color table, if it's present.
|
||||
uint32_t mBytesPerColor; // 3 or 4, depending on the format
|
||||
|
||||
// The number of bytes prior to the optional gap that have been read. This
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace image {
|
|||
|
||||
// Constants.
|
||||
static const uint32_t ICOHEADERSIZE = 6;
|
||||
static const uint32_t BITMAPINFOSIZE = 40;
|
||||
static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
|
||||
|
||||
// ----------------------------------------
|
||||
// Actual Data Processing
|
||||
|
@ -110,53 +110,16 @@ nsICODecoder::GetFinalStateFromContainedDecoder()
|
|||
MOZ_ASSERT(HasError() || !mCurrentFrame || mCurrentFrame->IsImageComplete());
|
||||
}
|
||||
|
||||
// Returns a buffer filled with the bitmap file header in little endian:
|
||||
// Signature 2 bytes 'BM'
|
||||
// FileSize 4 bytes File size in bytes
|
||||
// reserved 4 bytes unused (=0)
|
||||
// DataOffset 4 bytes File offset to Raster Data
|
||||
// Returns true if successful
|
||||
bool
|
||||
nsICODecoder::FillBitmapFileHeaderBuffer(int8_t* bfh)
|
||||
{
|
||||
memset(bfh, 0, 14);
|
||||
bfh[0] = 'B';
|
||||
bfh[1] = 'M';
|
||||
int32_t dataOffset = 0;
|
||||
int32_t fileSize = 0;
|
||||
dataOffset = bmp::FileHeader::LENGTH + BITMAPINFOSIZE;
|
||||
|
||||
// The color table is present only if BPP is <= 8
|
||||
if (mDirEntry.mBitCount <= 8) {
|
||||
uint16_t numColors = GetNumColors();
|
||||
if (numColors == (uint16_t)-1) {
|
||||
return false;
|
||||
}
|
||||
dataOffset += 4 * numColors;
|
||||
fileSize = dataOffset + GetRealWidth() * GetRealHeight();
|
||||
} else {
|
||||
fileSize = dataOffset + (mDirEntry.mBitCount * GetRealWidth() *
|
||||
GetRealHeight()) / 8;
|
||||
}
|
||||
|
||||
NativeEndian::swapToLittleEndianInPlace(&fileSize, 1);
|
||||
memcpy(bfh + 2, &fileSize, sizeof(fileSize));
|
||||
NativeEndian::swapToLittleEndianInPlace(&dataOffset, 1);
|
||||
memcpy(bfh + 10, &dataOffset, sizeof(dataOffset));
|
||||
return true;
|
||||
}
|
||||
|
||||
// A BMP inside of an ICO has *2 height because of the AND mask
|
||||
// that follows the actual bitmap. The BMP shouldn't know about
|
||||
// this difference though.
|
||||
bool
|
||||
nsICODecoder::FixBitmapHeight(int8_t* bih)
|
||||
{
|
||||
// Get the height from the BMP file information header
|
||||
int32_t height;
|
||||
memcpy(&height, bih + 8, sizeof(height));
|
||||
NativeEndian::swapFromLittleEndianInPlace(&height, 1);
|
||||
// BMPs can be stored inverted by having a negative height
|
||||
// Get the height from the BMP file information header.
|
||||
int32_t height = LittleEndian::readInt32(bih + 8);
|
||||
|
||||
// BMPs can be stored inverted by having a negative height.
|
||||
height = abs(height);
|
||||
|
||||
// The bitmap height is by definition * 2 what it should be to account for
|
||||
|
@ -176,8 +139,7 @@ nsICODecoder::FixBitmapHeight(int8_t* bih)
|
|||
}
|
||||
|
||||
// Fix the BMP height in the BIH so that the BMP decoder can work properly
|
||||
NativeEndian::swapToLittleEndianInPlace(&height, 1);
|
||||
memcpy(bih + 8, &height, sizeof(height));
|
||||
LittleEndian::writeInt32(bih + 8, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -186,15 +148,14 @@ nsICODecoder::FixBitmapHeight(int8_t* bih)
|
|||
bool
|
||||
nsICODecoder::FixBitmapWidth(int8_t* bih)
|
||||
{
|
||||
// Get the width from the BMP file information header
|
||||
int32_t width;
|
||||
memcpy(&width, bih + 4, sizeof(width));
|
||||
NativeEndian::swapFromLittleEndianInPlace(&width, 1);
|
||||
// Get the width from the BMP file information header.
|
||||
int32_t width = LittleEndian::readInt32(bih + 4);
|
||||
|
||||
if (width > 256) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should always trust the width from the bitmap itself instead of
|
||||
// We should always trust the width from the bitmap itself instead of
|
||||
// the ICO width.
|
||||
if (width == 256) {
|
||||
mDirEntry.mWidth = 0;
|
||||
|
@ -204,28 +165,6 @@ nsICODecoder::FixBitmapWidth(int8_t* bih)
|
|||
return true;
|
||||
}
|
||||
|
||||
// The BMP information header's bits per pixel should be trusted
|
||||
// more than what we have. Usually the ICO's BPP is set to 0.
|
||||
int32_t
|
||||
nsICODecoder::ReadBPP(const char* aBIH)
|
||||
{
|
||||
const int8_t* bih = reinterpret_cast<const int8_t*>(aBIH);
|
||||
int32_t bitsPerPixel;
|
||||
memcpy(&bitsPerPixel, bih + 14, sizeof(bitsPerPixel));
|
||||
NativeEndian::swapFromLittleEndianInPlace(&bitsPerPixel, 1);
|
||||
return bitsPerPixel;
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsICODecoder::ReadBIHSize(const char* aBIH)
|
||||
{
|
||||
const int8_t* bih = reinterpret_cast<const int8_t*>(aBIH);
|
||||
int32_t headerSize;
|
||||
memcpy(&headerSize, bih, sizeof(headerSize));
|
||||
NativeEndian::swapFromLittleEndianInPlace(&headerSize, 1);
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
LexerTransition<ICOState>
|
||||
nsICODecoder::ReadHeader(const char* aData)
|
||||
{
|
||||
|
@ -236,8 +175,7 @@ nsICODecoder::ReadHeader(const char* aData)
|
|||
mIsCursor = (aData[2] == 2);
|
||||
|
||||
// The fifth and sixth bytes specify the number of resources in the file.
|
||||
mNumIcons =
|
||||
LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aData + 4));
|
||||
mNumIcons = LittleEndian::readUint16(aData + 4);
|
||||
if (mNumIcons == 0) {
|
||||
return Transition::Terminate(ICOState::SUCCESS); // Nothing to do.
|
||||
}
|
||||
|
@ -271,19 +209,14 @@ nsICODecoder::ReadDirEntry(const char* aData)
|
|||
|
||||
// Read the directory entry.
|
||||
IconDirEntry e;
|
||||
memset(&e, 0, sizeof(e));
|
||||
memcpy(&e.mWidth, aData, sizeof(e.mWidth));
|
||||
memcpy(&e.mHeight, aData + 1, sizeof(e.mHeight));
|
||||
memcpy(&e.mColorCount, aData + 2, sizeof(e.mColorCount));
|
||||
memcpy(&e.mReserved, aData + 3, sizeof(e.mReserved));
|
||||
memcpy(&e.mPlanes, aData + 4, sizeof(e.mPlanes));
|
||||
e.mPlanes = LittleEndian::readUint16(&e.mPlanes);
|
||||
memcpy(&e.mBitCount, aData + 6, sizeof(e.mBitCount));
|
||||
e.mBitCount = LittleEndian::readUint16(&e.mBitCount);
|
||||
memcpy(&e.mBytesInRes, aData + 8, sizeof(e.mBytesInRes));
|
||||
e.mBytesInRes = LittleEndian::readUint32(&e.mBytesInRes);
|
||||
memcpy(&e.mImageOffset, aData + 12, sizeof(e.mImageOffset));
|
||||
e.mImageOffset = LittleEndian::readUint32(&e.mImageOffset);
|
||||
e.mWidth = aData[0];
|
||||
e.mHeight = aData[1];
|
||||
e.mColorCount = aData[2];
|
||||
e.mReserved = aData[3];
|
||||
e.mPlanes = LittleEndian::readUint16(aData + 4);
|
||||
e.mBitCount = LittleEndian::readUint16(aData + 6);
|
||||
e.mBytesInRes = LittleEndian::readUint32(aData + 8);
|
||||
e.mImageOffset = LittleEndian::readUint32(aData + 12);
|
||||
|
||||
// Determine if this is the biggest resource we've seen so far. We always use
|
||||
// the biggest resource for the intrinsic size, and if we're not downscaling,
|
||||
|
@ -389,21 +322,8 @@ nsICODecoder::SniffResource(const char* aData)
|
|||
ICOState::READ_PNG,
|
||||
toRead);
|
||||
} else {
|
||||
// Create a BMP decoder which will do most of the work for us; the exception
|
||||
// is the AND mask, which isn't present in standalone BMPs.
|
||||
nsBMPDecoder* bmpDecoder = new nsBMPDecoder(mImage);
|
||||
mContainedDecoder = bmpDecoder;
|
||||
bmpDecoder->SetIsWithinICO();
|
||||
mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
|
||||
mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
|
||||
mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
|
||||
if (mDownscaler) {
|
||||
mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
|
||||
}
|
||||
mContainedDecoder->Init();
|
||||
|
||||
// Make sure we have a sane size for the bitmap information header.
|
||||
int32_t bihSize = ReadBIHSize(aData);
|
||||
int32_t bihSize = LittleEndian::readUint32(aData);
|
||||
if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
|
||||
return Transition::Terminate(ICOState::FAILURE);
|
||||
}
|
||||
|
@ -439,22 +359,35 @@ nsICODecoder::ReadBIH(const char* aData)
|
|||
// Buffer the rest of the bitmap information header.
|
||||
memcpy(mBIHraw + PNGSIGNATURESIZE, aData, BITMAPINFOSIZE - PNGSIGNATURESIZE);
|
||||
|
||||
// Extracting the BPP from the BIH header; it should be trusted over the one
|
||||
// we have from the ICO header.
|
||||
mBPP = ReadBPP(mBIHraw);
|
||||
// Extract the BPP from the BIH header; it should be trusted over the one
|
||||
// we have from the ICO header which is usually set to 0.
|
||||
mBPP = LittleEndian::readUint16(mBIHraw + 14);
|
||||
|
||||
// The ICO format when containing a BMP does not include the 14 byte
|
||||
// bitmap file header. To use the code of the BMP decoder we need to
|
||||
// generate this header ourselves and feed it to the BMP decoder.
|
||||
int8_t bfhBuffer[BMPFILEHEADERSIZE];
|
||||
if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
|
||||
return Transition::Terminate(ICOState::FAILURE);
|
||||
// bitmap file header. So we create the BMP decoder via the constructor that
|
||||
// tells it to skip this, and pass in the required data (dataOffset) that
|
||||
// would have been present in the header.
|
||||
uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
|
||||
if (mDirEntry.mBitCount <= 8) {
|
||||
// The color table is present only if BPP is <= 8.
|
||||
uint16_t numColors = GetNumColors();
|
||||
if (numColors == (uint16_t)-1) {
|
||||
return Transition::Terminate(ICOState::FAILURE);
|
||||
}
|
||||
dataOffset += 4 * numColors;
|
||||
}
|
||||
|
||||
if (!WriteToContainedDecoder(reinterpret_cast<const char*>(bfhBuffer),
|
||||
sizeof(bfhBuffer))) {
|
||||
return Transition::Terminate(ICOState::FAILURE);
|
||||
// Create a BMP decoder which will do most of the work for us; the exception
|
||||
// is the AND mask, which isn't present in standalone BMPs.
|
||||
RefPtr<nsBMPDecoder> bmpDecoder = new nsBMPDecoder(mImage, dataOffset);
|
||||
mContainedDecoder = bmpDecoder;
|
||||
mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
|
||||
mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
|
||||
mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
|
||||
if (mDownscaler) {
|
||||
mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
|
||||
}
|
||||
mContainedDecoder->Init();
|
||||
|
||||
// Fix the ICO height from the BIH. It needs to be halved so our BMP decoder
|
||||
// will understand, because the BMP decoder doesn't expect the alpha mask that
|
||||
|
@ -473,14 +406,6 @@ nsICODecoder::ReadBIH(const char* aData)
|
|||
return Transition::Terminate(ICOState::FAILURE);
|
||||
}
|
||||
|
||||
// Sometimes the ICO BPP header field is not filled out so we should trust the
|
||||
// contained resource over our own information.
|
||||
// XXX(seth): Is this ever different than the value we obtained from
|
||||
// ReadBPP() above?
|
||||
RefPtr<nsBMPDecoder> bmpDecoder =
|
||||
static_cast<nsBMPDecoder*>(mContainedDecoder.get());
|
||||
mBPP = bmpDecoder->GetBitsPerPixel();
|
||||
|
||||
// Check to make sure we have valid color settings.
|
||||
uint16_t numColors = GetNumColors();
|
||||
if (numColors == uint16_t(-1)) {
|
||||
|
|
|
@ -87,8 +87,6 @@ private:
|
|||
// Gets decoder state from the contained decoder so it's visible externally.
|
||||
void GetFinalStateFromContainedDecoder();
|
||||
|
||||
// Creates a bitmap file header buffer, returns true if successful
|
||||
bool FillBitmapFileHeaderBuffer(int8_t* bfh);
|
||||
// Fixes the ICO height to match that of the BIH.
|
||||
// and also fixes the BIH height to be /2 of what it was.
|
||||
// See definition for explanation.
|
||||
|
@ -97,10 +95,6 @@ private:
|
|||
// Fixes the ICO width to match that of the BIH.
|
||||
// Returns false if invalid information is contained within.
|
||||
bool FixBitmapWidth(int8_t* bih);
|
||||
// Extract bitmap info header size count from BMP information header
|
||||
int32_t ReadBIHSize(const char* aBIH);
|
||||
// Extract bit count from BMP information header
|
||||
int32_t ReadBPP(const char* aBIH);
|
||||
// Calculates the row size in bytes for the AND mask table
|
||||
uint32_t CalcAlphaRowSize();
|
||||
// Obtains the number of colors from the BPP, mBPP must be filled in
|
||||
|
@ -120,7 +114,7 @@ private:
|
|||
StreamingLexer<ICOState, 32> mLexer; // The lexer.
|
||||
RefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
|
||||
UniquePtr<uint8_t[]> mMaskBuffer; // A temporary buffer for the alpha mask.
|
||||
char mBIHraw[40]; // The bitmap information header.
|
||||
char mBIHraw[bmp::InfoHeaderLength::WIN_ICO]; // The bitmap information header.
|
||||
IconDirEntry mDirEntry; // The dir entry for the selected resource.
|
||||
IntSize mBiggestResourceSize; // Used to select the intrinsic size.
|
||||
IntSize mBiggestResourceHotSpot; // Used to select the intrinsic size.
|
||||
|
|
|
@ -487,9 +487,9 @@ nsBMPEncoder::InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
|
|||
mBMPFileHeader.signature[1] = 'M';
|
||||
|
||||
if (aVersion == VERSION_3) {
|
||||
mBMPFileHeader.dataoffset = FileHeader::LENGTH + InfoHeaderLength::WIN_V3;
|
||||
mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V3;
|
||||
} else { // aVersion == 5
|
||||
mBMPFileHeader.dataoffset = FileHeader::LENGTH + InfoHeaderLength::WIN_V5;
|
||||
mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V5;
|
||||
}
|
||||
|
||||
// The color table is present only if BPP is <= 8
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "imgIEncoder.h"
|
||||
#include "BMPFileHeaders.h"
|
||||
#include "BMPHeaders.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
|
@ -23,6 +23,62 @@
|
|||
{0xbd, 0x16, 0xb0, 0x81, 0xa3, 0Xba, 0x8c, 0x0b} \
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace bmp {
|
||||
|
||||
struct FileHeader {
|
||||
char signature[2]; // String "BM".
|
||||
uint32_t filesize; // File size.
|
||||
int32_t reserved; // Zero.
|
||||
uint32_t dataoffset; // Offset to raster data.
|
||||
};
|
||||
|
||||
struct XYZ {
|
||||
int32_t x, y, z;
|
||||
};
|
||||
|
||||
struct XYZTriple {
|
||||
XYZ r, g, b;
|
||||
};
|
||||
|
||||
struct V5InfoHeader {
|
||||
uint32_t bihsize; // Header size
|
||||
int32_t width; // Uint16 in OS/2 BMPs
|
||||
int32_t height; // Uint16 in OS/2 BMPs
|
||||
uint16_t planes; // =1
|
||||
uint16_t bpp; // Bits per pixel.
|
||||
uint32_t compression; // See Compression for valid values
|
||||
uint32_t image_size; // (compressed) image size. Can be 0 if
|
||||
// compression==0
|
||||
uint32_t xppm; // Pixels per meter, horizontal
|
||||
uint32_t yppm; // Pixels per meter, vertical
|
||||
uint32_t colors; // Used Colors
|
||||
uint32_t important_colors; // Number of important colors. 0=all
|
||||
// The rest of the header is not available in WIN_V3 BMP Files
|
||||
uint32_t red_mask; // Bits used for red component
|
||||
uint32_t green_mask; // Bits used for green component
|
||||
uint32_t blue_mask; // Bits used for blue component
|
||||
uint32_t alpha_mask; // Bits used for alpha component
|
||||
uint32_t color_space; // 0x73524742=LCS_sRGB ...
|
||||
// These members are unused unless color_space == LCS_CALIBRATED_RGB
|
||||
XYZTriple white_point; // Logical white point
|
||||
uint32_t gamma_red; // Red gamma component
|
||||
uint32_t gamma_green; // Green gamma component
|
||||
uint32_t gamma_blue; // Blue gamma component
|
||||
uint32_t intent; // Rendering intent
|
||||
// These members are unused unless color_space == LCS_PROFILE_*
|
||||
uint32_t profile_offset; // Offset to profile data in bytes
|
||||
uint32_t profile_size; // Size of profile data in bytes
|
||||
uint32_t reserved; // =0
|
||||
|
||||
static const uint32_t COLOR_SPACE_LCS_SRGB = 0x73524742;
|
||||
};
|
||||
|
||||
} // namespace bmp
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
// Provides BMP encoding functionality. Use InitFromData() to do the
|
||||
// encoding. See that function definition for encoding options.
|
||||
|
||||
|
|
|
@ -162,10 +162,10 @@ nsICOEncoder::AddImageFrame(const uint8_t* aData,
|
|||
|
||||
// Icon files that wrap a BMP file must not include the BITMAPFILEHEADER
|
||||
// section at the beginning of the encoded BMP data, so we must skip over
|
||||
// bmp::FileHeader::LENGTH bytes when adding the BMP content to the icon
|
||||
// bmp::FILE_HEADER_LENGTH bytes when adding the BMP content to the icon
|
||||
// file.
|
||||
mICODirEntry.mBytesInRes =
|
||||
BMPImageBufferSize - bmp::FileHeader::LENGTH + andMaskSize;
|
||||
BMPImageBufferSize - bmp::FILE_HEADER_LENGTH + andMaskSize;
|
||||
|
||||
// Encode the icon headers
|
||||
EncodeFileHeader();
|
||||
|
@ -174,14 +174,14 @@ nsICOEncoder::AddImageFrame(const uint8_t* aData,
|
|||
char* imageBuffer;
|
||||
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
memcpy(mImageBufferCurr, imageBuffer + bmp::FileHeader::LENGTH,
|
||||
BMPImageBufferSize - bmp::FileHeader::LENGTH);
|
||||
memcpy(mImageBufferCurr, imageBuffer + bmp::FILE_HEADER_LENGTH,
|
||||
BMPImageBufferSize - bmp::FILE_HEADER_LENGTH);
|
||||
// We need to fix the BMP height to be *2 for the AND mask
|
||||
uint32_t fixedHeight = GetRealHeight() * 2;
|
||||
NativeEndian::swapToLittleEndianInPlace(&fixedHeight, 1);
|
||||
// The height is stored at an offset of 8 from the DIB header
|
||||
memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight));
|
||||
mImageBufferCurr += BMPImageBufferSize - bmp::FileHeader::LENGTH;
|
||||
mImageBufferCurr += BMPImageBufferSize - bmp::FILE_HEADER_LENGTH;
|
||||
|
||||
// Calculate rowsize in DWORD's
|
||||
uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
|
||||
|
|
|
@ -162,19 +162,19 @@ nsJPEGEncoder::InitFromData(const uint8_t* aData,
|
|||
jpeg_write_scanlines(&cinfo, const_cast<uint8_t**>(&row), 1);
|
||||
}
|
||||
} else if (aInputFormat == INPUT_FORMAT_RGBA) {
|
||||
uint8_t* row = new uint8_t[aWidth * 3];
|
||||
UniquePtr<uint8_t[]> rowptr = MakeUnique<uint8_t[]>(aWidth * 3);
|
||||
uint8_t* row = rowptr.get();
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
ConvertRGBARow(&aData[cinfo.next_scanline * aStride], row, aWidth);
|
||||
jpeg_write_scanlines(&cinfo, &row, 1);
|
||||
}
|
||||
delete[] row;
|
||||
} else if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
|
||||
uint8_t* row = new uint8_t[aWidth * 3];
|
||||
UniquePtr<uint8_t[]> rowptr = MakeUnique<uint8_t[]>(aWidth * 3);
|
||||
uint8_t* row = rowptr.get();
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
ConvertHostARGBRow(&aData[cinfo.next_scanline * aStride], row, aWidth);
|
||||
jpeg_write_scanlines(&cinfo, &row, 1);
|
||||
}
|
||||
delete[] row;
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
|
|
@ -287,22 +287,18 @@ nsPNGEncoder::AddImageFrame(const uint8_t* aData,
|
|||
if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
|
||||
// PNG requires RGBA with post-multiplied alpha, so we need to
|
||||
// convert
|
||||
uint8_t* row = new uint8_t[aWidth * 4];
|
||||
UniquePtr<uint8_t[]> row = MakeUnique<uint8_t[]>(aWidth * 4);
|
||||
for (uint32_t y = 0; y < aHeight; y++) {
|
||||
ConvertHostARGBRow(&aData[y * aStride], row, aWidth, useTransparency);
|
||||
png_write_row(mPNG, row);
|
||||
ConvertHostARGBRow(&aData[y * aStride], row.get(), aWidth, useTransparency);
|
||||
png_write_row(mPNG, row.get());
|
||||
}
|
||||
delete[] row;
|
||||
|
||||
} else if (aInputFormat == INPUT_FORMAT_RGBA && !useTransparency) {
|
||||
// RBGA, but we need to strip the alpha
|
||||
uint8_t* row = new uint8_t[aWidth * 4];
|
||||
UniquePtr<uint8_t[]> row = MakeUnique<uint8_t[]>(aWidth * 4);
|
||||
for (uint32_t y = 0; y < aHeight; y++) {
|
||||
StripAlpha(&aData[y * aStride], row, aWidth);
|
||||
png_write_row(mPNG, row);
|
||||
StripAlpha(&aData[y * aStride], row.get(), aWidth);
|
||||
png_write_row(mPNG, row.get());
|
||||
}
|
||||
delete[] row;
|
||||
|
||||
} else if (aInputFormat == INPUT_FORMAT_RGB ||
|
||||
aInputFormat == INPUT_FORMAT_RGBA) {
|
||||
// simple RBG(A), no conversion needed
|
||||
|
|
|
@ -323,6 +323,14 @@ typedef bool
|
|||
typedef bool
|
||||
(* JSEnumerateOp)(JSContext* cx, JS::HandleObject obj);
|
||||
|
||||
/**
|
||||
* The type of ObjectOps::funToString. This callback allows an object to
|
||||
* provide a custom string to use when Function.prototype.toString is invoked on
|
||||
* that object. A null return value means OOM.
|
||||
*/
|
||||
typedef JSString*
|
||||
(* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, unsigned indent);
|
||||
|
||||
/**
|
||||
* Resolve a lazy property named by id in obj by defining it directly in obj.
|
||||
* Lazy properties are those reflected from some peer native property space
|
||||
|
@ -650,6 +658,7 @@ struct ObjectOps
|
|||
GetElementsOp getElements;
|
||||
JSNewEnumerateOp enumerate;
|
||||
ThisValueOp thisValue;
|
||||
JSFunToStringOp funToString;
|
||||
};
|
||||
|
||||
#define JS_NULL_OBJECT_OPS \
|
||||
|
@ -665,7 +674,7 @@ typedef void (*JSClassInternal)();
|
|||
struct JSClass {
|
||||
JS_CLASS_MEMBERS(JSFinalizeOp);
|
||||
|
||||
void* reserved[25];
|
||||
void* reserved[26];
|
||||
};
|
||||
|
||||
#define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot
|
||||
|
|
|
@ -136,7 +136,7 @@ static const unsigned PushedRetAddr = 0;
|
|||
static const unsigned PushedFP = 0;
|
||||
static const unsigned StoredFP = 0;
|
||||
static const unsigned PostStorePrePopFP = 0;
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static const unsigned PushedRetAddr = 8;
|
||||
static const unsigned PushedFP = 24;
|
||||
static const unsigned StoredFP = 28;
|
||||
|
@ -157,7 +157,7 @@ PushRetAddr(MacroAssembler& masm)
|
|||
{
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
masm.push(lr);
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.push(ra);
|
||||
#else
|
||||
// The x86/x64 call instruction pushes the return address.
|
||||
|
@ -221,7 +221,8 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, AsmJSExit:
|
|||
Label* profilingReturn)
|
||||
{
|
||||
Register scratch = ABIArgGenerator::NonReturn_VolatileReg0;
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
Register scratch2 = ABIArgGenerator::NonReturn_VolatileReg1;
|
||||
#endif
|
||||
|
||||
|
@ -245,7 +246,8 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, AsmJSExit:
|
|||
// and the async interrupt exit. Since activation.fp can be read at any
|
||||
// time and still points to the current frame, be careful to only update
|
||||
// sp after activation.fp has been repointed to the caller's frame.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.loadPtr(Address(masm.getStackPointer(), 0), scratch2);
|
||||
masm.storePtr(scratch2, Address(scratch, AsmJSActivation::offsetOfFP()));
|
||||
DebugOnly<uint32_t> prePop = masm.currentOffset();
|
||||
|
@ -344,6 +346,13 @@ js::GenerateAsmJSFunctionEpilogue(MacroAssembler& masm, unsigned framePushed,
|
|||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -566,7 +575,7 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
|||
MOZ_ASSERT(offsetInModule < codeRange->end());
|
||||
uint32_t offsetInCodeRange = offsetInModule - codeRange->begin();
|
||||
void** sp = (void**)state.sp;
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
if (offsetInCodeRange < PushedRetAddr) {
|
||||
// First instruction of the ARM/MIPS function; the return address is
|
||||
// still in lr and fp still holds the caller's fp.
|
||||
|
|
|
@ -384,6 +384,17 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
|
|||
if (!staticLinkData_.relativeLinks.append(link))
|
||||
return false;
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
// On MIPS64 we need to update all the long jumps because they contain an
|
||||
// absolute adress.
|
||||
for (size_t i = 0; i < masm.numLongJumps(); i++) {
|
||||
RelativeLink link(RelativeLink::InstructionImmediate);
|
||||
link.patchAtOffset = masm.longJump(i);
|
||||
InstImm* inst = (InstImm*)(code_ + masm.longJump(i));
|
||||
link.targetOffset = Assembler::ExtractLoad64Value(inst) - (uint64_t)code_;
|
||||
if (!staticLinkData_.relativeLinks.append(link))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
|
@ -826,7 +837,7 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared*> heap, JSContext* cx)
|
|||
if (access.hasLengthCheck())
|
||||
X86Encoding::AddInt32(access.patchLengthAt(code_), heapLength);
|
||||
}
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
uint32_t heapLength = heap->byteLength();
|
||||
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
|
||||
jit::Assembler::UpdateBoundsCheck(heapLength,
|
||||
|
@ -1739,6 +1750,9 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext* cx)
|
|||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t));
|
||||
void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next());
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Instruction* instr = (Instruction*)(callerRetAddr - 6 * sizeof(uint32_t));
|
||||
void* callee = (void*)Assembler::ExtractLoad64Value(instr);
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
void* callee = nullptr;
|
||||
|
@ -1767,6 +1781,9 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext* cx)
|
|||
Assembler::WriteLuiOriInstructions(instr, instr->next(),
|
||||
ScratchRegister, (uint32_t)newCallee);
|
||||
instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)newCallee);
|
||||
instr[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
|
@ -1841,6 +1858,18 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext* cx)
|
|||
instr[1].makeNop();
|
||||
instr[2].makeNop();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Instruction* instr = (Instruction*)jump;
|
||||
if (enabled) {
|
||||
Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)profilingEpilogue);
|
||||
instr[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
|
||||
} else {
|
||||
instr[0].makeNop();
|
||||
instr[1].makeNop();
|
||||
instr[2].makeNop();
|
||||
instr[3].makeNop();
|
||||
instr[4].makeNop();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
|
@ -1881,6 +1910,7 @@ GetCPUID(uint32_t* cpuId)
|
|||
X64 = 0x2,
|
||||
ARM = 0x3,
|
||||
MIPS = 0x4,
|
||||
MIPS64 = 0x5,
|
||||
ARCH_BITS = 3
|
||||
};
|
||||
|
||||
|
@ -1900,6 +1930,10 @@ GetCPUID(uint32_t* cpuId)
|
|||
MOZ_ASSERT(GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = MIPS | (GetMIPSFlags() << ARCH_BITS);
|
||||
return true;
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
MOZ_ASSERT(GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = MIPS64 | (GetMIPSFlags() << ARCH_BITS);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
|
|
@ -746,7 +746,7 @@ class AsmJSModule
|
|||
|
||||
explicit RelativeLink(Kind kind)
|
||||
{
|
||||
#if defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
kind_ = kind;
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
// On ARM, CodeLabels are only used to label raw pointers, so in
|
||||
|
@ -757,14 +757,14 @@ class AsmJSModule
|
|||
}
|
||||
|
||||
bool isRawPointerPatch() {
|
||||
#if defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
return kind_ == RawPointer;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JS_CODEGEN_MIPS32
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
Kind kind_;
|
||||
#endif
|
||||
uint32_t patchAtOffset;
|
||||
|
|
|
@ -7055,7 +7055,7 @@ GenerateEntry(ModuleValidator& m, unsigned exportIndex)
|
|||
// Save the return address if it wasn't already saved by the call insn.
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
masm.push(lr);
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.push(ra);
|
||||
#elif defined(JS_CODEGEN_X86)
|
||||
static const unsigned EntryFrameSize = sizeof(void*);
|
||||
|
@ -7067,15 +7067,15 @@ GenerateEntry(ModuleValidator& m, unsigned exportIndex)
|
|||
masm.PushRegsInMask(NonVolatileRegs);
|
||||
MOZ_ASSERT(masm.framePushed() == FramePushedAfterSave);
|
||||
|
||||
// ARM and MIPS have a globally-pinned GlobalReg (x64 uses RIP-relative
|
||||
// ARM and MIPS/MIPS64 have a globally-pinned GlobalReg (x64 uses RIP-relative
|
||||
// addressing, x86 uses immediates in effective addresses). For the
|
||||
// AsmJSGlobalRegBias addition, see Assembler-(mips,arm).h.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.movePtr(IntArgReg1, GlobalReg);
|
||||
masm.addPtr(Imm32(AsmJSGlobalRegBias), GlobalReg);
|
||||
#endif
|
||||
|
||||
// ARM, MIPS and x64 have a globally-pinned HeapReg (x86 uses immediates in
|
||||
// ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
|
||||
// effective addresses). Loading the heap register depends on the global
|
||||
// register already having been loaded.
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
|
@ -7384,7 +7384,7 @@ GenerateFFIInterpExit(ModuleValidator& m, const Signature& sig, unsigned exitInd
|
|||
return !masm.oom() && m.finishGeneratingInterpExit(exitIndex, &begin, &profilingReturn);
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static const unsigned MaybeSavedGlobalReg = sizeof(void*);
|
||||
#else
|
||||
static const unsigned MaybeSavedGlobalReg = 0;
|
||||
|
@ -7428,7 +7428,7 @@ GenerateFFIIonExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex,
|
|||
m.masm().append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
|
||||
#elif defined(JS_CODEGEN_X86)
|
||||
m.masm().append(AsmJSGlobalAccess(masm.movlWithPatch(Imm32(0), callee), globalDataOffset));
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.computeEffectiveAddress(Address(GlobalReg, globalDataOffset - AsmJSGlobalRegBias), callee);
|
||||
#endif
|
||||
|
||||
|
@ -7464,7 +7464,7 @@ GenerateFFIIonExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex,
|
|||
// so they must be explicitly preserved. Only save GlobalReg since
|
||||
// HeapReg must be reloaded (from global data) after the call since the
|
||||
// heap may change during the FFI call.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
|
||||
masm.storePtr(GlobalReg, Address(masm.getStackPointer(), ionFrameBytes));
|
||||
#endif
|
||||
|
@ -7580,7 +7580,7 @@ GenerateFFIIonExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex,
|
|||
}
|
||||
|
||||
// Reload the global register since Ion code can clobber any register.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
|
||||
masm.loadPtr(Address(masm.getStackPointer(), ionFrameBytes), GlobalReg);
|
||||
#endif
|
||||
|
@ -7895,7 +7895,7 @@ GenerateAsyncInterruptExit(ModuleValidator& m, Label* throwLabel)
|
|||
masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP)
|
||||
masm.popFlags(); // after this, nothing that sets conditions
|
||||
masm.ret(); // pop resumePC into PC
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
// Reserve space to store resumePC.
|
||||
masm.subFromStackPtr(Imm32(sizeof(intptr_t)));
|
||||
// set to zero so we can use masm.framePushed() below.
|
||||
|
|
|
@ -276,7 +276,14 @@ class LifoAlloc
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void* allocInfallible(size_t n) {
|
||||
void* allocInfallibleOrAssert(size_t n) {
|
||||
void* result = allocImpl(n);
|
||||
MOZ_RELEASE_ASSERT(result, "[OOM] Is it really infallible?");
|
||||
return result;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void* allocInfallibleOrCrash(size_t n) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (void* result = allocImpl(n))
|
||||
return result;
|
||||
|
@ -284,6 +291,11 @@ class LifoAlloc
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void* allocInfallible(size_t n) {
|
||||
return allocInfallibleOrCrash(n);
|
||||
}
|
||||
|
||||
// Ensures that enough space exists to satisfy N bytes worth of
|
||||
// allocation requests, not necessarily contiguous. Note that this does
|
||||
// not guarantee a successful single allocation of N bytes.
|
||||
|
|
|
@ -5195,6 +5195,10 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
*/
|
||||
bool isForDecl = false;
|
||||
|
||||
// True if a 'let' token at the head is parsed as an identifier instead of
|
||||
// as starting a declaration.
|
||||
bool letIsIdentifier = false;
|
||||
|
||||
/* Non-null when isForDecl is true for a 'for (let ...)' statement. */
|
||||
RootedStaticBlockObject blockObj(context);
|
||||
|
||||
|
@ -5222,19 +5226,38 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
isForDecl = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
pn1 = variables(yieldHandling, PNK_VAR, InForInit);
|
||||
} else if (tt == TOK_LET || tt == TOK_CONST) {
|
||||
} else if (tt == TOK_LET || tt == TOK_CONST ||
|
||||
(tt == TOK_NAME && tokenStream.nextName() == context->names().let)) {
|
||||
handler.disableSyntaxParser();
|
||||
bool constDecl = tt == TOK_CONST;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
isForDecl = true;
|
||||
blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return null();
|
||||
// Initialize the enclosing scope manually for the call to
|
||||
// |variables| below.
|
||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
||||
pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
|
||||
nullptr, blockObj, DontHoistVars);
|
||||
|
||||
// Check for the backwards-compatibility corner case in sloppy
|
||||
// mode like |for (let in e)| where the 'let' token should be
|
||||
// parsed as an identifier.
|
||||
bool parseDecl;
|
||||
if (tt == TOK_NAME) {
|
||||
if (!peekShouldParseLetDeclaration(&parseDecl, TokenStream::Operand))
|
||||
return null();
|
||||
letIsIdentifier = !parseDecl;
|
||||
} else {
|
||||
parseDecl = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
}
|
||||
|
||||
if (parseDecl) {
|
||||
bool constDecl = tt == TOK_CONST;
|
||||
isForDecl = true;
|
||||
blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return null();
|
||||
|
||||
// Initialize the enclosing scope manually for the call to
|
||||
// |variables| below.
|
||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
||||
pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
|
||||
nullptr, blockObj, DontHoistVars);
|
||||
} else {
|
||||
pn1 = expr(InProhibited, yieldHandling, TripledotProhibited);
|
||||
}
|
||||
} else {
|
||||
// Pass |InProhibited| when parsing an expression so that |in|
|
||||
// isn't parsed in a RelationalExpression as a binary operator.
|
||||
|
@ -5309,9 +5332,17 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
bool isForIn, isForOf;
|
||||
if (!matchInOrOf(&isForIn, &isForOf))
|
||||
return null();
|
||||
|
||||
// In for-in loops, a 'let' token may be used as an identifier for
|
||||
// backwards-compatibility reasons, e.g., |for (let in e)|. In for-of
|
||||
// loops, a 'let' token is never parsed as an identifier. Forbid
|
||||
// trying to parse a for-of loop if we have parsed a 'let' token as an
|
||||
// identifier above.
|
||||
//
|
||||
// See ES6 13.7.5.1.
|
||||
if (isForIn)
|
||||
headKind = PNK_FORIN;
|
||||
else if (isForOf)
|
||||
else if (isForOf && !letIsIdentifier)
|
||||
headKind = PNK_FOROF;
|
||||
}
|
||||
|
||||
|
@ -5567,12 +5598,12 @@ Parser<SyntaxParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
isForDecl = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl);
|
||||
}
|
||||
else if (tt == TOK_CONST || tt == TOK_LET) {
|
||||
} else if (tt == TOK_CONST || tt == TOK_LET ||
|
||||
(tt == TOK_NAME && tokenStream.nextName() == context->names().let))
|
||||
{
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited);
|
||||
}
|
||||
if (!lhsNode)
|
||||
|
@ -6627,6 +6658,71 @@ Parser<SyntaxParseHandler>::classDefinition(YieldHandling yieldHandling,
|
|||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::shouldParseLetDeclaration(bool* parseDeclOut)
|
||||
{
|
||||
// 'let' is a reserved keyword in strict mode and we shouldn't get here.
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
|
||||
TokenKind tt;
|
||||
*parseDeclOut = false;
|
||||
|
||||
if (!tokenStream.peekToken(&tt))
|
||||
return false;
|
||||
|
||||
switch (tt) {
|
||||
case TOK_NAME:
|
||||
// |let let| is disallowed per ES6 13.3.1.1.
|
||||
*parseDeclOut = tokenStream.nextName() != context->names().let;
|
||||
break;
|
||||
|
||||
case TOK_LC:
|
||||
case TOK_LB:
|
||||
// A following name is always a declaration.
|
||||
//
|
||||
// |let {| and |let [| are destructuring declarations.
|
||||
*parseDeclOut = true;
|
||||
break;
|
||||
|
||||
case TOK_LP:
|
||||
// Only parse let blocks for 1.7 and 1.8. Do not expose deprecated let
|
||||
// blocks to content.
|
||||
*parseDeclOut = versionNumber() == JSVERSION_1_7 || versionNumber() == JSVERSION_1_8;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::peekShouldParseLetDeclaration(bool* parseDeclOut,
|
||||
TokenStream::Modifier modifier)
|
||||
{
|
||||
*parseDeclOut = false;
|
||||
|
||||
#ifdef DEBUG
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, modifier))
|
||||
return false;
|
||||
MOZ_ASSERT(tt == TOK_NAME && tokenStream.nextName() == context->names().let);
|
||||
#endif
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_NAME, modifier);
|
||||
if (!shouldParseLetDeclaration(parseDeclOut))
|
||||
return false;
|
||||
|
||||
// Unget the TOK_NAME of 'let' if not parsing a declaration.
|
||||
if (!*parseDeclOut)
|
||||
tokenStream.ungetToken();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
|
||||
|
@ -6696,6 +6792,16 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
|||
}
|
||||
|
||||
case TOK_NAME: {
|
||||
// 'let' is a contextual keyword in sloppy node. In strict mode, it is
|
||||
// always lexed as TOK_LET.
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
bool parseDecl;
|
||||
if (!shouldParseLetDeclaration(&parseDecl))
|
||||
return null();
|
||||
if (parseDecl)
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
}
|
||||
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
|
|
@ -800,6 +800,15 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
ParseNodeKind headKind);
|
||||
bool checkForHeadConstInitializers(Node pn1);
|
||||
|
||||
// Use when the current token is TOK_NAME and is known to be 'let'.
|
||||
bool shouldParseLetDeclaration(bool* parseDeclOut);
|
||||
|
||||
// Use when the lookahead token is TOK_NAME and is known to be 'let'. If a
|
||||
// let declaration should be parsed, the TOK_NAME token of 'let' is
|
||||
// consumed. Otherwise, the current token remains the TOK_NAME token of
|
||||
// 'let'.
|
||||
bool peekShouldParseLetDeclaration(bool* parseDeclOut, TokenStream::Modifier modifier);
|
||||
|
||||
public:
|
||||
enum FunctionCallBehavior {
|
||||
PermitAssignmentToFunctionCalls,
|
||||
|
|
|
@ -999,6 +999,11 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||
|
||||
if (kw->tokentype != TOK_STRICT_RESERVED) {
|
||||
if (kw->version <= versionNumber()) {
|
||||
// Treat 'let' as an identifier and contextually a keyword in
|
||||
// sloppy mode. It is always a keyword in strict mode.
|
||||
if (kw->tokentype == TOK_LET && !strictMode())
|
||||
return true;
|
||||
|
||||
// Working keyword.
|
||||
if (ttp) {
|
||||
*ttp = kw->tokentype;
|
||||
|
@ -1006,12 +1011,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||
}
|
||||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
||||
}
|
||||
|
||||
// The keyword is not in this version. Treat it as an identifier, unless
|
||||
// it is let which we treat as TOK_STRICT_RESERVED by falling through to
|
||||
// the code below (ES5 forbids it in strict mode).
|
||||
if (kw->tokentype != TOK_LET)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Strict reserved word.
|
||||
|
|
|
@ -365,6 +365,13 @@ class MOZ_STACK_CLASS TokenStream
|
|||
return currentToken().name();
|
||||
}
|
||||
|
||||
PropertyName* nextName() const {
|
||||
if (nextToken().type == TOK_YIELD)
|
||||
return cx->names().yield;
|
||||
MOZ_ASSERT(nextToken().type == TOK_NAME);
|
||||
return nextToken().name();
|
||||
}
|
||||
|
||||
bool isCurrentTokenAssignment() const {
|
||||
return TokenKindIsAssignment(currentToken().type);
|
||||
}
|
||||
|
@ -999,12 +1006,12 @@ class MOZ_STACK_CLASS TokenStream
|
|||
void updateLineInfoForEOL();
|
||||
void updateFlagsForEOL();
|
||||
|
||||
const Token& nextToken() {
|
||||
const Token& nextToken() const {
|
||||
MOZ_ASSERT(hasLookahead());
|
||||
return tokens[(cursor + 1) & ntokensMask];
|
||||
}
|
||||
|
||||
bool hasLookahead() { return lookahead > 0; }
|
||||
bool hasLookahead() const { return lookahead > 0; }
|
||||
|
||||
// Options used for parsing/tokenizing.
|
||||
const ReadOnlyCompileOptions& options_;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче