This commit is contained in:
Carsten "Tomcat" Book 2014-04-08 15:41:27 +02:00
Родитель f16a25d558 d5067407dd
Коммит 5980987263
83 изменённых файлов: 1603 добавлений и 626 удалений

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="70b698c2e8d1764a1e27527a102df6452e405b9a"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>

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

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
<project name="platform_build" path="build" remote="b2g" revision="52c909ccead537f8f9dbf634f3e6639078a8b0bd">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="70b698c2e8d1764a1e27527a102df6452e405b9a"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>

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

@ -18,7 +18,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="70b698c2e8d1764a1e27527a102df6452e405b9a"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>

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

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "65cb0c455f058c49aead7de9a245d90aa890276c",
"revision": "82aa7db46a22bc99998468aa433eafb33d739ed6",
"repo_path": "/integration/gaia-central"
}

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

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="70b698c2e8d1764a1e27527a102df6452e405b9a"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>

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

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1958454595b1fa0e061f0652ae965629993f5708"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4645b8da5a9cf25795313b20a56bad67225786c5"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -4,18 +4,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MessagePort.h"
#include "MessageEvent.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/MessageChannel.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/MessagePortList.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "nsGlobalWindow.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "nsPresContext.h"
#include "nsIDocument.h"
#include "nsIDOMFile.h"
#include "nsIDOMFileList.h"
#include "nsIDOMMessageEvent.h"
#include "nsIPresShell.h"
namespace mozilla {
@ -91,6 +92,7 @@ struct StructuredCloneInfo
{
PostMessageRunnable* mEvent;
MessagePort* mPort;
nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts;
};
static JSObject*
@ -100,9 +102,6 @@ PostMessageReadStructuredClone(JSContext* cx,
uint32_t data,
void* closure)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
NS_ASSERTION(!data, "Data should be empty");
@ -119,22 +118,6 @@ PostMessageReadStructuredClone(JSContext* cx,
}
}
if (tag == SCTAG_DOM_MESSAGEPORT) {
NS_ASSERTION(!data, "Data should be empty");
MessagePort* port;
if (JS_ReadBytes(reader, &port, sizeof(port))) {
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
if (global) {
JS::Rooted<JSObject*> obj(cx, port->WrapObject(cx, global));
if (JS_WrapObject(cx, &obj)) {
port->BindToOwner(scInfo->mPort->GetOwner());
return obj;
}
}
}
}
const JSStructuredCloneCallbacks* runtimeCallbacks =
js::GetContextStructuredCloneCallbacks(cx);
@ -178,20 +161,6 @@ PostMessageWriteStructuredClone(JSContext* cx,
}
}
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, obj, port);
if (NS_SUCCEEDED(rv)) {
nsRefPtr<MessagePortBase> newPort = port->Clone();
if (!newPort) {
return false;
}
return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) &&
JS_WriteBytes(writer, &newPort, sizeof(newPort)) &&
scInfo->mEvent->StoreISupports(newPort);
}
const JSStructuredCloneCallbacks* runtimeCallbacks =
js::GetContextStructuredCloneCallbacks(cx);
@ -202,14 +171,108 @@ PostMessageWriteStructuredClone(JSContext* cx,
return false;
}
static bool
PostMessageReadTransferStructuredClone(JSContext* aCx,
JSStructuredCloneReader* reader,
uint32_t tag, void* data,
uint64_t unused,
void* aClosure,
JS::MutableHandle<JSObject*> returnObject)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
MessagePort* port = static_cast<MessagePort*>(data);
port->BindToOwner(scInfo->mPort->GetOwner());
scInfo->mPorts.Put(port, nullptr);
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
if (global) {
JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, global));
if (JS_WrapObject(aCx, &obj)) {
MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
returnObject.set(obj);
}
}
return true;
}
return false;
}
static bool
PostMessageTransferStructuredClone(JSContext* aCx,
JS::Handle<JSObject*> aObj,
void* aClosure,
uint32_t* aTag,
JS::TransferableOwnership* aOwnership,
void** aContent,
uint64_t *aExtraData)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
NS_ASSERTION(scInfo, "Must have scInfo!");
MessagePortBase *port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
if (NS_SUCCEEDED(rv)) {
nsRefPtr<MessagePortBase> newPort;
if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) {
// No duplicate.
return false;
}
newPort = port->Clone();
scInfo->mPorts.Put(port, newPort);
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
*aOwnership = JS::SCTAG_TMO_CUSTOM;
*aContent = newPort;
*aExtraData = 0;
return true;
}
return false;
}
static void
PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
void* aData,
uint64_t aExtraData,
void* aClosure)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM);
nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData));
scInfo->mPorts.Remove(port);
}
}
JSStructuredCloneCallbacks kPostMessageCallbacks = {
PostMessageReadStructuredClone,
PostMessageWriteStructuredClone,
nullptr
nullptr,
PostMessageReadTransferStructuredClone,
PostMessageTransferStructuredClone,
PostMessageFreeTransferStructuredClone
};
} // anonymous namespace
static PLDHashOperator
PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
{
nsTArray<nsRefPtr<MessagePortBase> > *array =
static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
array->AppendElement(aKey);
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
PostMessageRunnable::Run()
{
@ -226,45 +289,32 @@ PostMessageRunnable::Run()
// Deserialize the structured clone data
JS::Rooted<JS::Value> messageData(cx);
{
StructuredCloneInfo scInfo;
scInfo.mEvent = this;
scInfo.mPort = mPort;
StructuredCloneInfo scInfo;
scInfo.mEvent = this;
scInfo.mPort = mPort;
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
// Create the event
nsIDocument* doc = mPort->GetOwner()->GetExtantDoc();
if (!doc) {
return NS_OK;
}
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
do_QueryInterface(mPort->GetOwner());
nsRefPtr<MessageEvent> event =
new MessageEvent(eventTarget, nullptr, nullptr);
ErrorResult error;
nsRefPtr<Event> event =
doc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), error);
if (error.Failed()) {
return NS_OK;
}
event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */,
false /* cancelable */, messageData, EmptyString(),
EmptyString(), nullptr);
event->SetTrusted(true);
event->SetSource(mPort);
nsCOMPtr<nsIDOMMessageEvent> message = do_QueryInterface(event);
nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
true /* cancelable */,
messageData,
EmptyString(),
EmptyString(),
mPort->GetOwner());
if (NS_FAILED(rv)) {
return NS_OK;
}
message->SetTrusted(true);
nsTArray<nsRefPtr<MessagePortBase> > ports;
scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports);
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
bool status;
mPort->DispatchEvent(event, &status);
mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status);
return status ? NS_OK : NS_ERROR_FAILURE;
}

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

@ -28,7 +28,7 @@ enum StructuredCloneTags {
// These tags are used for both main thread and workers.
SCTAG_DOM_IMAGEDATA,
SCTAG_DOM_MESSAGEPORT,
SCTAG_DOM_MAP_MESSAGEPORT,
SCTAG_DOM_FUNCTION,

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

@ -56,6 +56,7 @@
#include "nsLayoutStatics.h"
#include "nsCCUncollectableMarker.h"
#include "mozilla/dom/workers/Workers.h"
#include "mozilla/dom/MessagePortList.h"
#include "nsJSPrincipals.h"
#include "mozilla/Attributes.h"
#include "mozilla/Debug.h"
@ -63,6 +64,7 @@
#include "mozilla/EventStates.h"
#include "mozilla/MouseEvents.h"
#include "AudioChannelService.h"
#include "MessageEvent.h"
// Interfaces Needed
#include "nsIFrame.h"
@ -82,7 +84,6 @@
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMEvent.h"
#include "nsIDOMMessageEvent.h"
#include "nsIDOMPopupBlockedEvent.h"
#include "nsIDOMPopStateEvent.h"
#include "nsIDOMHashChangeEvent.h"
@ -7645,6 +7646,7 @@ struct StructuredCloneInfo {
PostMessageEvent* event;
bool subsumes;
nsPIDOMWindow* window;
nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
};
static JSObject*
@ -7654,9 +7656,6 @@ PostMessageReadStructuredClone(JSContext* cx,
uint32_t data,
void* closure)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
NS_ASSERTION(!data, "Data should be empty");
@ -7673,22 +7672,6 @@ PostMessageReadStructuredClone(JSContext* cx,
}
}
if (MessageChannel::PrefEnabled() && tag == SCTAG_DOM_MESSAGEPORT) {
NS_ASSERTION(!data, "Data should be empty");
MessagePortBase* port;
if (JS_ReadBytes(reader, &port, sizeof(port))) {
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
if (global) {
JS::Rooted<JSObject*> obj(cx, port->WrapObject(cx, global));
if (JS_WrapObject(cx, &obj)) {
port->BindToOwner(scInfo->window);
return obj;
}
}
}
}
const JSStructuredCloneCallbacks* runtimeCallbacks =
js::GetContextStructuredCloneCallbacks(cx);
@ -7729,22 +7712,6 @@ PostMessageWriteStructuredClone(JSContext* cx,
scInfo->event->StoreISupports(supports);
}
if (MessageChannel::PrefEnabled()) {
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, obj, port);
if (NS_SUCCEEDED(rv) && scInfo->subsumes) {
nsRefPtr<MessagePortBase> newPort = port->Clone();
if (!newPort) {
return false;
}
return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) &&
JS_WriteBytes(writer, &newPort, sizeof(newPort)) &&
scInfo->event->StoreISupports(newPort);
}
}
const JSStructuredCloneCallbacks* runtimeCallbacks =
js::GetContextStructuredCloneCallbacks(cx);
@ -7755,14 +7722,108 @@ PostMessageWriteStructuredClone(JSContext* cx,
return false;
}
static bool
PostMessageReadTransferStructuredClone(JSContext* aCx,
JSStructuredCloneReader* reader,
uint32_t tag, void* aData,
uint64_t aExtraData,
void* aClosure,
JS::MutableHandle<JSObject*> returnObject)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (MessageChannel::PrefEnabled() && tag == SCTAG_DOM_MAP_MESSAGEPORT) {
MessagePort* port = static_cast<MessagePort*>(aData);
port->BindToOwner(scInfo->window);
scInfo->ports.Put(port, nullptr);
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
if (global) {
JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, global));
if (JS_WrapObject(aCx, &obj)) {
MOZ_ASSERT(port->GetOwner() == scInfo->window);
returnObject.set(obj);
}
}
return true;
}
return false;
}
static bool
PostMessageTransferStructuredClone(JSContext* aCx,
JS::Handle<JSObject*> aObj,
void* aClosure,
uint32_t* aTag,
JS::TransferableOwnership* aOwnership,
void** aContent,
uint64_t* aExtraData)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (MessageChannel::PrefEnabled()) {
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
if (NS_SUCCEEDED(rv)) {
nsRefPtr<MessagePortBase> newPort;
if (scInfo->ports.Get(port, getter_AddRefs(newPort))) {
// No duplicate.
return false;
}
newPort = port->Clone();
scInfo->ports.Put(port, newPort);
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
*aOwnership = JS::SCTAG_TMO_CUSTOM;
*aContent = newPort;
*aExtraData = 0;
return true;
}
}
return false;
}
void
PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
void *aContent, uint64_t aExtraData, void* aClosure)
{
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
NS_ASSERTION(scInfo, "Must have scInfo!");
if (MessageChannel::PrefEnabled() && aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
scInfo->ports.Remove(port);
}
}
JSStructuredCloneCallbacks kPostMessageCallbacks = {
PostMessageReadStructuredClone,
PostMessageWriteStructuredClone,
nullptr
nullptr,
PostMessageReadTransferStructuredClone,
PostMessageTransferStructuredClone,
PostMessageFreeTransferStructuredClone
};
} // anonymous namespace
static PLDHashOperator
PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
{
nsTArray<nsRefPtr<MessagePortBase> > *array =
static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
array->AppendElement(aKey);
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
PostMessageEvent::Run()
{
@ -7817,38 +7878,27 @@ PostMessageEvent::Run()
// Deserialize the structured clone data
JS::Rooted<JS::Value> messageData(cx);
{
StructuredCloneInfo scInfo;
scInfo.event = this;
scInfo.window = targetWindow;
StructuredCloneInfo scInfo;
scInfo.event = this;
scInfo.window = targetWindow;
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
// Create the event
nsIDocument* doc = targetWindow->mDoc;
if (!doc)
return NS_OK;
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
nsRefPtr<MessageEvent> event =
new MessageEvent(eventTarget, nullptr, nullptr);
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
nsCOMPtr<nsIDOMEvent> event;
domDoc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), getter_AddRefs(event));
if (!event)
return NS_OK;
nsCOMPtr<nsIDOMMessageEvent> message = do_QueryInterface(event);
nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
true /* cancelable */,
messageData,
mCallerOrigin,
EmptyString(),
mSource);
if (NS_FAILED(rv))
return NS_OK;
event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
false /*cancelable */, messageData, mCallerOrigin,
EmptyString(), mSource);
nsTArray<nsRefPtr<MessagePortBase> > ports;
scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
// We can't simply call dispatchEvent on the window because doing so ends
// up flipping the trusted bit on the event, and we don't want that to
@ -7860,14 +7910,14 @@ PostMessageEvent::Run()
if (shell)
presContext = shell->GetPresContext();
message->SetTrusted(mTrustedCaller);
WidgetEvent* internalEvent = message->GetInternalNSEvent();
event->SetTrusted(mTrustedCaller);
WidgetEvent* internalEvent = event->GetInternalNSEvent();
nsEventStatus status = nsEventStatus_eIgnore;
EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
presContext,
internalEvent,
message,
static_cast<dom::Event*>(event.get()),
&status);
return NS_OK;
}

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

@ -2881,7 +2881,10 @@ nsJSContext::EnsureStatics()
static JSStructuredCloneCallbacks cloneCallbacks = {
NS_DOMReadStructuredClone,
NS_DOMWriteStructuredClone,
NS_DOMStructuredCloneError
NS_DOMStructuredCloneError,
nullptr,
nullptr,
nullptr
};
JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);

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

@ -64,7 +64,7 @@ nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
mSize = 0;
mVersion = 0;
JS_ClearStructuredClone(jsBytes, mSize);
JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr);
return NS_ERROR_FAILURE;
}
else {
@ -73,7 +73,7 @@ nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
memcpy(mData, jsBytes, mSize);
JS_ClearStructuredClone(jsBytes, mSize);
JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr);
return NS_OK;
}

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

@ -13,7 +13,7 @@
ok (evt.data.port instanceof MessagePort, "Data contains a MessagePort");
var a = new MessageChannel();
window.parent.postMessage({ status: "FINISH", port: a.port2 }, '*');
window.parent.postMessage({ status: "FINISH", port: a.port2 }, '*', [a.port2]);
}
</script>

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

@ -16,7 +16,7 @@
if (counter++ == 0) {
ok(!(evt.data % 2), "The number " + evt.data + " has been received correctly by the iframe");
window.parent.postMessage({ type: 'PORT', port: port }, '*');
window.parent.postMessage({ type: 'PORT', port: port }, '*', [port]);
}
else {
ok(false, "Wrong message!");

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

@ -33,7 +33,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=913761
}
}
transportChannel.port2.postMessage({ port: serviceChannel.port2} /* TODO: [serviceChannel.port2] */);
transportChannel.port2.postMessage({ port: serviceChannel.port2}, [serviceChannel.port2]);
}
SimpleTest.waitForExplicitFinish();

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

@ -45,7 +45,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
div.appendChild(ifr);
function iframeLoaded() {
ifr.contentWindow.postMessage({ port: a.port2 }, '*');
ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
}
}

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

@ -40,7 +40,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
ok(evt.data % 2, "The number " + evt.data + " has been received correctly by the main window");
if (evt.data < MAX - 1) {
ifr.contentWindow.postMessage({ type: 'PORT', port: port }, '*');
ifr.contentWindow.postMessage({ type: 'PORT', port: port }, '*', [port]);
} else {
SimpleTest.finish();
}
@ -66,7 +66,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
div.appendChild(ifr);
function iframeLoaded() {
ifr.contentWindow.postMessage({ type: 'PORT', port: a.port2 }, '*');
ifr.contentWindow.postMessage({ type: 'PORT', port: a.port2 }, '*', [a.port2]);
}
}

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

@ -38,7 +38,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
div.appendChild(ifr);
function iframeLoaded() {
ifr.contentWindow.postMessage({ port: a.port2 }, '*');
ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
}
var tests = [ 42,

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

@ -213,7 +213,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
}
}
postMessage(a.port2, '*');
postMessage(a.port2, '*', [a.port2]);
}
var tests = [

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

@ -38,7 +38,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
div.appendChild(ifr);
function iframeLoaded() {
ifr.contentWindow.postMessage({ port: a.port2 }, '*');
ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
}
a.port1.addEventListener('message', receivePortMessage, false);

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

@ -0,0 +1,115 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=912456
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 912456 - port cloning</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=912456">Mozilla Bug 912456</a>
<script type="application/javascript">
function testTransfer() {
var a = new MessageChannel();
ok(a, "MessageChannel created");
window.addEventListener('message', receiveMessage, false);
function receiveMessage(evt) {
ok(evt.data.port, "Port has been received!");
var a = new MessageChannel();
ok(a, "MessageChannel created");
try {
evt.data.port.postMessage({port: a.port2});
ok(false, "PostMessage should throw! - no transfered port");
} catch(e) {
ok(true, "PostMessage should throw! - no transfered port");
}
try {
evt.data.port.postMessage({port: a.port2}, [a.port2, a.port2]);
ok(false, "PostMessage should throw - no duplicate!");
} catch(e) {
ok(true, "PostMessage should throw - no duplicate!");
}
evt.data.port.postMessage({port: a.port2}, [a.port2]);
}
a.port1.onmessage = function(evt) {
ok(evt.data.port, "Port has been received!");
window.removeEventListener('message', receiveMessage);
runTest();
}
try {
postMessage({ port: a.port2}, 42, '*');
ok(false, "PostMessage should throw! - no transfered port");
} catch(e) {
ok(true, "PostMessage should throw! - no transfered port");
}
try {
postMessage({ port: a.port2}, 42, '*', [a.port2, a.port2]);
ok(false, "PostMessage should throw - no duplicate!");
} catch(e) {
ok(true, "PostMessage should throw - no duplicate!");
}
postMessage({port: a.port2}, '*', [a.port2]);
}
function testPorts() {
var a = new MessageChannel();
ok(a, "MessageChannel created");
window.addEventListener('message', receiveMessage, false);
function receiveMessage(evt) {
ok(evt.data, "Data is 42");
ok(evt.ports, "Port is received");
is(evt.ports.length, 1, "Ports.length is 1");
var a = new MessageChannel();
ok(a, "MessageChannel created");
evt.ports[0].postMessage(42, [a.port2]);
}
a.port1.onmessage = function(evt) {
ok(evt.data, "Data is 42");
ok(evt.ports, "Port is received");
is(evt.ports.length, 1, "Ports.length is 1");
window.removeEventListener('message', receiveMessage);
runTest();
}
postMessage(42, '*', [a.port2]);
}
var tests = [
testTransfer,
testPorts
];
function runTest() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
</script>
</body>
</html>

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

@ -9,6 +9,7 @@
#include "mozilla/dom/Event.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMMessageEvent.h"
#include "mozilla/dom/MessagePortList.h"
namespace mozilla {
namespace dom {
@ -57,6 +58,17 @@ public:
void SetPorts(MessagePortList* aPorts);
// Non WebIDL methods
void SetSource(mozilla::dom::MessagePort* aPort)
{
mPortSource = aPort;
}
void SetSource(nsPIDOMWindow* aWindow)
{
mWindowSource = aWindow;
}
static already_AddRefed<MessageEvent>
Constructor(const GlobalObject& aGlobal, JSContext* aCx,
const nsAString& aType,

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

@ -1343,6 +1343,9 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
JSStructuredCloneCallbacks callbacks = {
IDBObjectStore::StructuredCloneReadCallback<MainThreadDeserializationTraits>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
};
@ -1364,6 +1367,9 @@ IDBObjectStore::SerializeValue(JSContext* aCx,
JSStructuredCloneCallbacks callbacks = {
nullptr,
StructuredCloneWriteCallback,
nullptr,
nullptr,
nullptr,
nullptr
};
@ -4529,6 +4535,9 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
JSStructuredCloneCallbacks callbacks = {
IDBObjectStore::StructuredCloneReadCallback<CreateIndexDeserializationTraits>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
};

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

@ -152,7 +152,10 @@ Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
JSStructuredCloneCallbacks gCallbacks = {
Read,
Write,
Error
Error,
nullptr,
nullptr,
nullptr
};
} // anonymous namespace

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

@ -2512,20 +2512,23 @@ RadioInterface.prototype = {
}).bind(this));
},
setCellBroadcastSearchList: function(newSearchListStr) {
if (newSearchListStr == this._cellBroadcastSearchListStr) {
setCellBroadcastSearchList: function(newSearchList) {
if ((newSearchList == this._cellBroadcastSearchList) ||
(newSearchList && this._cellBroadcastSearchList &&
newSearchList.gsm == this._cellBroadcastSearchList.gsm &&
newSearchList.cdma == this._cellBroadcastSearchList.cdma)) {
return;
}
this.workerMessenger.send("setCellBroadcastSearchList",
{ searchListStr: newSearchListStr },
{ searchList: newSearchList },
(function callback(response) {
if (!response.success) {
let lock = gSettingsService.createLock();
lock.set(kSettingsCellBroadcastSearchList,
this._cellBroadcastSearchListStr, null);
this._cellBroadcastSearchList, null);
} else {
this._cellBroadcastSearchListStr = response.searchListStr;
this._cellBroadcastSearchList = response.searchList;
}
return false;
@ -3384,7 +3387,7 @@ RadioInterface.prototype = {
_sntp: null,
// Cell Broadcast settings values.
_cellBroadcastSearchListStr: null,
_cellBroadcastSearchList: null,
// Operator's mcc-mnc.
_lastKnownNetwork: null,
@ -3471,9 +3474,12 @@ RadioInterface.prototype = {
break;
case kSettingsCellBroadcastSearchList:
if (DEBUG) {
this.debug("'" + kSettingsCellBroadcastSearchList + "' is now " + aResult);
this.debug("'" + kSettingsCellBroadcastSearchList +
"' is now " + JSON.stringify(aResult));
}
this.setCellBroadcastSearchList(aResult);
// TODO: Set searchlist for Multi-SIM. See Bug 921326.
let result = Array.isArray(aResult) ? aResult[0] : aResult;
this.setCellBroadcastSearchList(result);
break;
}
},

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

@ -1832,8 +1832,19 @@ RilObject.prototype = {
},
setCellBroadcastSearchList: function(options) {
let getSearchListStr = function(aSearchList) {
if (typeof aSearchList === "string" || aSearchList instanceof String) {
return aSearchList;
}
// TODO: Set search list for CDMA/GSM individually. Bug 990926
let prop = this._isCdma ? "cdma" : "gsm";
return aSearchList && aSearchList[prop];
}.bind(this);
try {
let str = options.searchListStr;
let str = getSearchListStr(options.searchList);
this.cellBroadcastConfigs.MMI = this._convertCellBroadcastSearchList(str);
options.success = true;
} catch (e) {

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

@ -137,3 +137,37 @@ add_test(function test_ril_worker_cellbroadcast_merge_config() {
run_next_test();
});
add_test(function test_ril_worker_cellbroadcast_set_search_list() {
let worker = newWorker({
postRILMessage: function(id, parcel) {
// Do nothing
},
postMessage: function(message) {
// Do nothing
}
});
let context = worker.ContextPool._contexts[0];
function test(aIsCdma, aSearchList, aExpected) {
context.RIL._isCdma = aIsCdma;
let options = { searchList: aSearchList };
context.RIL.setCellBroadcastSearchList(options);
// Enforce the MMI result to string for comparison.
do_check_eq("" + context.RIL.cellBroadcastConfigs.MMI, aExpected);
do_check_eq(options.success, true);
}
let searchListStr = "1,2,3,4";
let searchList = { gsm: "1,2,3,4", cdma: "5,6,7,8" };
test(false, searchListStr, "1,2,2,3,3,4,4,5");
test(true, searchListStr, "1,2,2,3,3,4,4,5");
test(false, searchList, "1,2,2,3,3,4,4,5");
test(true, searchList, "5,6,6,7,7,8,8,9");
test(false, null, "null");
test(true, null, "null");
run_next_test();
});

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

@ -447,7 +447,10 @@ struct WorkerStructuredCloneCallbacks
JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
WorkerStructuredCloneCallbacks::Read,
WorkerStructuredCloneCallbacks::Write,
WorkerStructuredCloneCallbacks::Error
WorkerStructuredCloneCallbacks::Error,
nullptr,
nullptr,
nullptr
};
struct MainThreadWorkerStructuredCloneCallbacks
@ -606,7 +609,10 @@ struct MainThreadWorkerStructuredCloneCallbacks
JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
MainThreadWorkerStructuredCloneCallbacks::Read,
MainThreadWorkerStructuredCloneCallbacks::Write,
MainThreadWorkerStructuredCloneCallbacks::Error
MainThreadWorkerStructuredCloneCallbacks::Error,
nullptr,
nullptr,
nullptr
};
struct ChromeWorkerStructuredCloneCallbacks
@ -636,7 +642,10 @@ struct ChromeWorkerStructuredCloneCallbacks
JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
ChromeWorkerStructuredCloneCallbacks::Read,
ChromeWorkerStructuredCloneCallbacks::Write,
ChromeWorkerStructuredCloneCallbacks::Error
ChromeWorkerStructuredCloneCallbacks::Error,
nullptr,
nullptr,
nullptr
};
struct MainThreadChromeWorkerStructuredCloneCallbacks
@ -694,7 +703,10 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = {
MainThreadChromeWorkerStructuredCloneCallbacks::Read,
MainThreadChromeWorkerStructuredCloneCallbacks::Write,
MainThreadChromeWorkerStructuredCloneCallbacks::Error
MainThreadChromeWorkerStructuredCloneCallbacks::Error,
nullptr,
nullptr,
nullptr
};
class MainThreadReleaseRunnable MOZ_FINAL : public nsRunnable

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

@ -64,8 +64,7 @@ public:
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mShuttingDownOnIOThread);
RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
Close(); // will also remove fd from I/O loop
mShuttingDownOnIOThread = true;
}
@ -643,7 +642,7 @@ void
UnixSocketImpl::OnSocketCanReceiveWithoutBlocking()
{
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
// Read all of the incoming data.
while (true) {
@ -691,7 +690,7 @@ void
UnixSocketImpl::OnSocketCanSendWithoutBlocking()
{
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
// Try to write the bytes of mCurrentRilRawData. If all were written, continue.
//
@ -739,6 +738,8 @@ UnixSocketConsumer::UnixSocketConsumer() : mImpl(nullptr)
UnixSocketConsumer::~UnixSocketConsumer()
{
MOZ_ASSERT(mConnectionStatus == SOCKET_DISCONNECTED);
MOZ_ASSERT(!mImpl);
}
bool

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

@ -135,7 +135,7 @@ enum SocketConnectionStatus {
SOCKET_CONNECTED = 3
};
class UnixSocketConsumer : public RefCounted<UnixSocketConsumer>
class UnixSocketConsumer : public AtomicRefCounted<UnixSocketConsumer>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(UnixSocketConsumer)

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

@ -23,6 +23,32 @@ struct JSStructuredCloneWriter;
// API for the HTML5 internal structured cloning algorithm.
namespace JS {
enum TransferableOwnership {
// Transferable data has not been filled in yet
SCTAG_TMO_UNFILLED = 0,
// Structured clone buffer does not yet own the data
SCTAG_TMO_UNOWNED = 1,
// All values at least this large are owned by the clone buffer
SCTAG_TMO_FIRST_OWNED = 2,
// Data is a pointer that can be freed
SCTAG_TMO_ALLOC_DATA = 2,
// Data is a SharedArrayBufferObject's buffer
SCTAG_TMO_SHARED_BUFFER = 3,
// Data is embedding-specific. The engine can free it by calling the
// freeTransfer op. The embedding can also use SCTAG_TMO_USER_MIN and
// greater, up to 32 bits, to distinguish specific ownership variants.
SCTAG_TMO_CUSTOM = 4,
SCTAG_TMO_USER_MIN
};
} /* namespace JS */
// Read structured data from the reader r. This hook is used to read a value
// previously serialized by a call to the WriteStructuredCloneOp hook.
//
@ -43,13 +69,52 @@ typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReade
//
// Return true on success, false on error/exception.
typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w,
JS::HandleObject obj, void *closure);
JS::HandleObject obj, void *closure);
// This is called when JS_WriteStructuredClone is given an invalid transferable.
// To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
// with error set to one of the JS_SCERR_* values.
typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid);
// This is called when JS_ReadStructuredClone receives a transferable object
// not known to the engine. If this hook does not exist or returns false, the
// JS engine calls the reportError op if set, otherwise it throws a
// DATA_CLONE_ERR DOM Exception. This method is called before any other
// callback and must return a non-null object in returnObject on success.
typedef bool (*ReadTransferStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r,
uint32_t tag, void *content, uint64_t extraData,
void *closure,
JS::MutableHandleObject returnObject);
// Called when JS_WriteStructuredClone receives a transferable object not
// handled by the engine. If this hook does not exist or returns false, the JS
// engine will call the reportError hook or fall back to throwing a
// DATA_CLONE_ERR DOM Exception. This method is called before any other
// callback.
//
// tag: indicates what type of transferable this is. Must be greater than
// 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY)
//
// ownership: see TransferableOwnership, above. Used to communicate any needed
// ownership info to the FreeTransferStructuredCloneOp.
//
// content, extraData: what the ReadTransferStructuredCloneOp will receive
//
typedef bool (*TransferStructuredCloneOp)(JSContext *cx,
JS::Handle<JSObject*> obj,
void *closure,
// Output:
uint32_t *tag,
JS::TransferableOwnership *ownership,
void **content,
uint64_t *extraData);
// Called when JS_ClearStructuredClone has to free an unknown transferable
// object. Note that it should never trigger a garbage collection (and will
// assert in a debug build if it does.)
typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
void *content, uint64_t extraData, void *closure);
// The maximum supported structured-clone serialization format version. Note
// that this does not need to be bumped for Transferable-only changes, since
// they are never saved to persistent storage.
@ -59,6 +124,9 @@ struct JSStructuredCloneCallbacks {
ReadStructuredCloneOp read;
WriteStructuredCloneOp write;
StructuredCloneErrorOp reportError;
ReadTransferStructuredCloneOp readTransfer;
TransferStructuredCloneOp writeTransfer;
FreeTransferStructuredCloneOp freeTransfer;
};
// Note: if the *data contains transferable objects, it can be read only once.
@ -68,14 +136,16 @@ JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t ve
const JSStructuredCloneCallbacks *optionalCallbacks, void *closure);
// Note: On success, the caller is responsible for calling
// JS_ClearStructuredClone(*datap, nbytesp).
// JS_ClearStructuredClone(*datap, nbytes, optionalCallbacks, closure).
JS_PUBLIC_API(bool)
JS_WriteStructuredClone(JSContext *cx, JS::HandleValue v, uint64_t **datap, size_t *nbytesp,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure, JS::HandleValue transferable);
JS_PUBLIC_API(bool)
JS_ClearStructuredClone(const uint64_t *data, size_t nbytes);
JS_ClearStructuredClone(uint64_t *data, size_t nbytes,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure);
JS_PUBLIC_API(bool)
JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable);
@ -89,10 +159,19 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
uint64_t *data_;
size_t nbytes_;
uint32_t version_;
const JSStructuredCloneCallbacks *callbacks_;
void *closure_;
public:
JSAutoStructuredCloneBuffer()
: data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {}
: data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
callbacks_(nullptr), closure_(nullptr)
{}
JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks *callbacks, void *closure)
: data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
callbacks_(callbacks), closure_(closure)
{}
JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer &&other);
JSAutoStructuredCloneBuffer &operator=(JSAutoStructuredCloneBuffer &&other);
@ -128,8 +207,8 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
private:
// Copy and assignment are not supported.
JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other);
JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other);
JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other) MOZ_DELETE;
JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other) MOZ_DELETE;
};
// The range of tag values the application may use for its own custom object types.

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

@ -35,10 +35,18 @@ namespace mozilla {}
namespace js {}
/*
* Pattern used to overwrite freed memory. If you are accessing an object with
* this pattern, you probably have a dangling pointer.
* Patterns used by SpiderMonkey to overwrite unused memory. If you are
* accessing an object with one of these pattern, you probably have a dangling
* pointer.
*/
#define JS_FREE_PATTERN 0xDA
#define JS_FRESH_NURSERY_PATTERN 0x2F
#define JS_SWEPT_NURSERY_PATTERN 0x2B
#define JS_ALLOCATED_NURSERY_PATTERN 0x2D
#define JS_FRESH_TENURED_PATTERN 0x4F
#define JS_SWEPT_TENURED_PATTERN 0x4B
#define JS_ALLOCATED_TENURED_PATTERN 0x4D
#define JS_SWEPT_CODE_PATTERN 0x3b
#define JS_SWEPT_FRAME_PATTERN 0x5b
#define JS_ASSERT(expr) MOZ_ASSERT(expr)
#define JS_ASSERT_IF(cond, expr) MOZ_ASSERT_IF(cond, expr)

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

@ -36,6 +36,8 @@
#if ENABLE_ASSEMBLER
#include "jsutil.h"
// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid
// instruction address on the platform (for example, check any alignment requirements).
#if WTF_CPU_ARM_THUMB2
@ -199,7 +201,7 @@ public:
if (!m_executablePool)
return;
memset(m_code.executableAddress(), JS_FREE_PATTERN, m_allocSize);
JS_POISON(m_code.executableAddress(), JS_SWEPT_CODE_PATTERN, m_allocSize);
m_code = MacroAssemblerCodePtr();

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

@ -192,6 +192,14 @@ GetBuildConfiguration(JSContext *cx, unsigned argc, jsval *vp)
if (!JS_SetProperty(cx, info, "binary-data", value))
return false;
#ifdef EXPOSE_INTL_API
value = BooleanValue(true);
#else
value = BooleanValue(false);
#endif
if (!JS_SetProperty(cx, info, "intl-api", value))
return false;
args.rval().setObject(*info);
return true;
}
@ -1274,7 +1282,7 @@ class CloneBufferObject : public JSObject {
// Discard an owned clone buffer.
void discard() {
if (data())
JS_ClearStructuredClone(data(), nbytes());
JS_ClearStructuredClone(data(), nbytes(), nullptr, nullptr);
setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
}

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

@ -288,6 +288,7 @@ struct FreeSpan
return nullptr;
}
checkSpan();
JS_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
return reinterpret_cast<void *>(thing);
}

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

@ -97,17 +97,36 @@ static void MarkChildren(JSTracer *trc, jit::JitCode *code);
/*** Object Marking ***/
#ifdef DEBUG
#if defined(DEBUG)
template<typename T>
static inline bool
IsThingPoisoned(T *thing)
{
const uint8_t pb = JS_FREE_PATTERN;
const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
JS_STATIC_ASSERT(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint32_t));
uint32_t *p =
reinterpret_cast<uint32_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
return *p == pw;
static_assert(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint32_t),
"Ensure it is well defined to look past any free span that "
"may be embedded in the thing's header when freed.");
const uint8_t poisonBytes[] = {
JS_FRESH_NURSERY_PATTERN,
JS_SWEPT_NURSERY_PATTERN,
JS_ALLOCATED_NURSERY_PATTERN,
JS_FRESH_TENURED_PATTERN,
JS_SWEPT_TENURED_PATTERN,
JS_ALLOCATED_TENURED_PATTERN,
JS_SWEPT_CODE_PATTERN,
JS_SWEPT_FRAME_PATTERN
};
const int numPoisonBytes = sizeof(poisonBytes) / sizeof(poisonBytes[0]);
uint32_t *p = reinterpret_cast<uint32_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
// Note: all free patterns are odd to make the common, not-poisoned case a single test.
if ((*p & 1) == 0)
return false;
for (int i = 0; i < numPoisonBytes; ++i) {
const uint8_t pb = poisonBytes[i];
const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
if (*p == pw)
return true;
}
return false;
}
#endif

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

@ -62,9 +62,7 @@ js::Nursery::init()
currentStart_ = start();
rt->gcNurseryEnd_ = chunk(LastNurseryChunk).end();
numActiveChunks_ = 1;
#ifdef JS_GC_ZEAL
JS_POISON(heap, FreshNursery, NurserySize);
#endif
JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, NurserySize);
setCurrentChunk(0);
updateDecommittedRegion();
@ -170,9 +168,7 @@ js::Nursery::allocate(size_t size)
void *thing = (void *)position();
position_ = position() + size;
#ifdef JS_GC_ZEAL
JS_POISON(thing, AllocatedThing, size);
#endif
JS_POISON(thing, JS_ALLOCATED_NURSERY_PATTERN, size);
return thing;
}
@ -701,6 +697,8 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
if (isEmpty())
return;
rt->gcStats.count(gcstats::STAT_MINOR_GC);
TIME_START(total);
AutoStopVerifyingBarriers av(rt, false);
@ -877,7 +875,7 @@ js::Nursery::sweep(JSRuntime *rt)
{
#ifdef JS_GC_ZEAL
/* Poison the nursery contents so touching a freed object will crash. */
JS_POISON((void *)start(), SweptNursery, NurserySize - sizeof(JSRuntime *));
JS_POISON((void *)start(), JS_SWEPT_NURSERY_PATTERN, NurserySize);
for (int i = 0; i < NumNurseryChunks; ++i)
chunk(i).trailer.runtime = runtime();
@ -890,6 +888,11 @@ js::Nursery::sweep(JSRuntime *rt)
} else
#endif
{
#ifdef JS_CRASH_DIAGNOSTICS
JS_POISON((void *)start(), JS_SWEPT_NURSERY_PATTERN, allocationEnd() - start());
for (int i = 0; i < numActiveChunks_; ++i)
chunk(i).trailer.runtime = runtime();
#endif
setCurrentChunk(0);
}
@ -900,7 +903,9 @@ js::Nursery::sweep(JSRuntime *rt)
void
js::Nursery::growAllocableSpace()
{
#ifdef JS_GC_ZEAL
MOZ_ASSERT_IF(runtime()->gcZeal_ == ZealGenerationalGCValue, numActiveChunks_ == NumNurseryChunks);
#endif
numActiveChunks_ = Min(numActiveChunks_ * 2, NumNurseryChunks);
}

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

@ -293,9 +293,6 @@ class Nursery
* In debug and zeal builds, these bytes indicate the state of an unused
* segment of nursery-allocated memory.
*/
static const uint8_t FreshNursery = 0x2a;
static const uint8_t SweptNursery = 0x2b;
static const uint8_t AllocatedThing = 0x2c;
void enterZealMode() {
if (isEnabled())
numActiveChunks_ = NumNurseryChunks;

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

@ -365,6 +365,7 @@ Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp)
ss.appendNumber("Zones Collected", "%d", "", collectedCount);
ss.appendNumber("Total Zones", "%d", "", zoneCount);
ss.appendNumber("Total Compartments", "%d", "", compartmentCount);
ss.appendNumber("Minor GCs", "%d", "", counts[STAT_MINOR_GC]);
ss.appendNumber("MMU (20ms)", "%d", "%", int(mmu20 * 100));
ss.appendNumber("MMU (50ms)", "%d", "%", int(mmu50 * 100));
ss.appendDecimal("SCC Sweep Total", "ms", t(sccTotal));

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

@ -68,6 +68,7 @@ enum Phase {
enum Stat {
STAT_NEW_CHUNK,
STAT_DESTROY_CHUNK,
STAT_MINOR_GC,
STAT_LIMIT
};

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

@ -6706,6 +6706,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
}
Register code = regs.takeAny();
masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallScripted::offsetOfGetter()), callee);
masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame);
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
masm.loadBaselineOrIonRaw(code, code, SequentialExecution, &failureLeaveStubFrame);
@ -7600,6 +7601,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
}
Register code = regs.takeAny();
masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfSetter()), callee);
masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame);
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
masm.loadBaselineOrIonRaw(code, code, SequentialExecution, &failureLeaveStubFrame);

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

@ -742,7 +742,7 @@ JitCode::finalize(FreeOp *fop)
// Don't do this if the Ion code is protected, as the signal handler will
// deadlock trying to reacquire the interrupt lock.
if (fop->runtime()->jitRuntime() && !fop->runtime()->jitRuntime()->ionCodeProtected())
memset(code_, JS_FREE_PATTERN, bufferSize_);
memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_);
code_ = nullptr;
// Code buffers are stored inside JSC pools.

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

@ -2141,7 +2141,7 @@ js::ArrayShiftMoveElements(JSObject *obj)
* themselves.
*/
uint32_t initlen = obj->getDenseInitializedLength();
obj->moveDenseElementsUnbarriered(0, 1, initlen);
obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
}
/* ES5 15.4.4.9 */

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

@ -484,7 +484,7 @@ Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
if (!newFreeSpanStart)
newFreeSpanStart = thing;
t->finalize(fop);
JS_POISON(t, JS_FREE_PATTERN, thingSize);
JS_POISON(t, JS_SWEPT_TENURED_PATTERN, thingSize);
}
}
}
@ -493,6 +493,7 @@ Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
JS_ASSERT(newListTail == &newListHead);
JS_ASSERT(!newFreeSpanStart ||
newFreeSpanStart == thingsStart(thingKind));
JS_POISON(data, JS_SWEPT_TENURED_PATTERN, sizeof(data));
return true;
}
@ -781,7 +782,7 @@ Chunk::prepareToBeFreed(JSRuntime *rt)
void
Chunk::init(JSRuntime *rt)
{
JS_POISON(this, JS_FREE_PATTERN, ChunkSize);
JS_POISON(this, JS_FRESH_TENURED_PATTERN, ChunkSize);
/*
* We clear the bitmap to guard against xpc_IsGrayGCThing being called on

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

@ -1472,8 +1472,8 @@ FinalizeGenerator(FreeOp *fop, JSObject *obj)
gen->state == JSGEN_OPEN);
// If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
if (gen->fp)
JS_POISON(gen->fp, JS_FREE_PATTERN, sizeof(InterpreterFrame));
JS_POISON(gen, JS_FREE_PATTERN, sizeof(JSGenerator));
JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame));
JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator));
fop->free_(gen);
}

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

@ -725,13 +725,14 @@ class JSObject : public js::ObjectImpl
}
}
void moveDenseElementsUnbarriered(uint32_t dstStart, uint32_t srcStart, uint32_t count) {
void moveDenseElementsNoPreBarrier(uint32_t dstStart, uint32_t srcStart, uint32_t count) {
JS_ASSERT(!shadowZone()->needsBarrier());
JS_ASSERT(dstStart + count <= getDenseCapacity());
JS_ASSERT(srcStart + count <= getDenseCapacity());
memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value));
DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count);
}
bool shouldConvertDoubleElements() {

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

@ -278,7 +278,7 @@ ClearAllBitArrayElements(size_t *array, size_t length)
#ifdef DEBUG
# define JS_CRASH_DIAGNOSTICS 1
#endif
#ifdef JS_CRASH_DIAGNOSTICS
#if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
# define JS_POISON(p, val, size) memset((p), (val), (size))
#else
# define JS_POISON(p, val, size) ((void) 0)

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

@ -10,6 +10,12 @@ var summary =
print(BUGNUMBER + ": " + summary);
if (typeof Intl !== 'object' && typeof quit == 'function') {
print("Test skipped");
reportCompare(true, true);
quit(0);
}
/**************
* BEGIN TEST *
**************/

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

@ -340,7 +340,18 @@ def run_test_remote(test, device, prefix, options):
# the same buffer to both.
return TestOutput(test, cmd, out, out, returncode, None, False)
def check_output(out, err, rc, test):
def check_output(out, err, rc, timed_out, test):
if timed_out:
# The shell sometimes hangs on shutdown on Windows 7 and Windows
# Server 2008. See bug 970063 comment 7 for a description of the
# problem. Until bug 956899 is fixed, ignore timeouts on these
# platforms (versions 6.0 and 6.1).
if sys.platform == 'win32':
ver = sys.getwindowsversion()
if ver.major == 6 and ver.minor <= 1:
return True
return False
if test.expect_error:
# The shell exits with code 3 on uncaught exceptions.
# Sometimes 0 is returned on Windows for unknown reasons.
@ -582,7 +593,7 @@ def process_test_results(results, num_tests, options):
if res.test.valgrind:
sys.stdout.write(res.err)
ok = check_output(res.out, res.err, res.rc, res.test)
ok = check_output(res.out, res.err, res.rc, res.timed_out, res.test)
doing = 'after %s' % res.test.relpath_tests
if not ok:
failures.append(res)

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

@ -70,8 +70,8 @@ enum StructuredDataType {
SCTAG_STRING_OBJECT,
SCTAG_NUMBER_OBJECT,
SCTAG_BACK_REFERENCE_OBJECT,
SCTAG_DO_NOT_USE_1,
SCTAG_DO_NOT_USE_2,
SCTAG_DO_NOT_USE_1, // Required for backwards compatibility
SCTAG_DO_NOT_USE_2, // Required for backwards compatibility
SCTAG_TYPED_ARRAY_OBJECT,
SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + ScalarTypeDescr::TYPE_INT8,
@ -91,11 +91,24 @@ enum StructuredDataType {
* require bumping JS_STRUCTURED_CLONE_VERSION.
*/
SCTAG_TRANSFER_MAP_HEADER = 0xFFFF0200,
SCTAG_TRANSFER_MAP_ENTRY,
SCTAG_TRANSFER_MAP_PENDING_ENTRY,
SCTAG_TRANSFER_MAP_ARRAY_BUFFER,
SCTAG_TRANSFER_MAP_SHARED_BUFFER,
SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES,
SCTAG_END_OF_BUILTIN_TYPES
};
/*
* Format of transfer map:
* <SCTAG_TRANSFER_MAP_HEADER, TransferableMapHeader(UNREAD|TRANSFERRED)>
* numTransferables (64 bits)
* array of:
* <SCTAG_TRANSFER_MAP_*, TransferableOwnership>
* pointer (64 bits)
* extraData (64 bits), eg byte length for ArrayBuffers
*/
// Data associated with an SCTAG_TRANSFER_MAP_HEADER that tells whether the
// contents have been read out yet or not.
enum TransferableMapHeader {
@ -103,29 +116,17 @@ enum TransferableMapHeader {
SCTAG_TM_TRANSFERRED
};
enum TransferableObjectType {
// Transferable data has not been filled in yet
SCTAG_TM_UNFILLED = 0,
// Structured clone buffer does not yet own the data
SCTAG_TM_UNOWNED = 1,
// All values at least this large are owned by the clone buffer
SCTAG_TM_FIRST_OWNED = 2,
// Data is a pointer to a SharedArrayRawBuffer.
SCTAG_TM_SHARED_BUFFER = 2,
// Data is a pointer that can be freed
SCTAG_TM_ALLOC_DATA = 3,
};
static inline uint64_t
PairToUInt64(uint32_t tag, uint32_t data)
{
return uint64_t(data) | (uint64_t(tag) << 32);
}
namespace js {
struct SCOutput {
public:
explicit SCOutput(JSContext *cx);
~SCOutput();
JSContext *context() const { return cx; }
@ -146,16 +147,20 @@ struct SCOutput {
private:
JSContext *cx;
js::Vector<uint64_t> buf;
Vector<uint64_t> buf;
};
struct SCInput {
class SCInput {
public:
SCInput(JSContext *cx, uint64_t *data, size_t nbytes);
JSContext *context() const { return cx; }
static void getPtr(const uint64_t *buffer, void **ptr);
static void getPair(const uint64_t *buffer, uint32_t *tagp, uint32_t *datap);
bool read(uint64_t *p);
bool readNativeEndian(uint64_t *p);
bool readPair(uint32_t *tagp, uint32_t *datap);
bool readDouble(double *p);
bool readBytes(void *p, size_t nbytes);
@ -165,21 +170,19 @@ struct SCInput {
bool get(uint64_t *p);
bool getPair(uint32_t *tagp, uint32_t *datap);
bool replace(uint64_t u);
bool replacePair(uint32_t tag, uint32_t data);
uint64_t *tell() const { return point; }
void seek(uint64_t *pos) {
JS_ASSERT(pos <= end);
point = pos;
}
uint64_t *end() const { return bufEnd; }
template <class T>
bool readArray(T *p, size_t nelems);
private:
bool eof();
bool reportTruncated() {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
return false;
}
private:
void staticAssertions() {
JS_STATIC_ASSERT(sizeof(jschar) == 2);
JS_STATIC_ASSERT(sizeof(uint32_t) == 4);
@ -188,20 +191,20 @@ struct SCInput {
JSContext *cx;
uint64_t *point;
uint64_t *end;
uint64_t *bufEnd;
};
}
} /* namespace js */
struct JSStructuredCloneReader {
public:
explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb,
explicit JSStructuredCloneReader(SCInput &in, const JSStructuredCloneCallbacks *cb,
void *cbClosure)
: in(in), objs(in.context()), allObjs(in.context()),
callbacks(cb), closure(cbClosure) { }
js::SCInput &input() { return in; }
bool read(js::Value *vp);
SCInput &input() { return in; }
bool read(Value *vp);
private:
JSContext *context() { return in.context(); }
@ -210,19 +213,19 @@ struct JSStructuredCloneReader {
bool checkDouble(double d);
JSString *readString(uint32_t nchars);
bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false);
bool readArrayBuffer(uint32_t nbytes, js::Value *vp);
bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp);
bool readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, bool v1Read = false);
bool readArrayBuffer(uint32_t nbytes, Value *vp);
bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, Value *vp);
bool readId(jsid *idp);
bool startRead(js::Value *vp);
bool startRead(Value *vp);
js::SCInput &in;
SCInput &in;
// Stack of objects with properties remaining to be read.
js::AutoValueVector objs;
AutoValueVector objs;
// Stack of all objects read during this deserialization
js::AutoValueVector allObjs;
AutoValueVector allObjs;
// The user defined callbacks that will be used for cloning.
const JSStructuredCloneCallbacks *callbacks;
@ -230,25 +233,31 @@ struct JSStructuredCloneReader {
// Any value passed to JS_ReadStructuredClone.
void *closure;
friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, JS::MutableHandleValue vp);
friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, MutableHandleValue vp);
};
struct JSStructuredCloneWriter {
public:
explicit JSStructuredCloneWriter(js::SCOutput &out,
explicit JSStructuredCloneWriter(JSContext *cx,
const JSStructuredCloneCallbacks *cb,
void *cbClosure,
jsval tVal)
: out(out), objs(out.context()),
: out(cx), objs(out.context()),
counts(out.context()), ids(out.context()),
memory(out.context()), callbacks(cb), closure(cbClosure),
transferable(out.context(), tVal), transferableObjects(out.context()) { }
~JSStructuredCloneWriter();
bool init() { return memory.init() && parseTransferable() && writeTransferMap(); }
bool write(const js::Value &v);
bool write(const Value &v);
js::SCOutput &output() { return out; }
SCOutput &output() { return out; }
bool extractBuffer(uint64_t **datap, size_t *sizep) {
return out.extractBuffer(datap, sizep);
}
private:
JSContext *context() { return out.context(); }
@ -257,37 +266,37 @@ struct JSStructuredCloneWriter {
bool writeString(uint32_t tag, JSString *str);
bool writeId(jsid id);
bool writeArrayBuffer(JS::HandleObject obj);
bool writeTypedArray(JS::HandleObject obj);
bool startObject(JS::HandleObject obj, bool *backref);
bool startWrite(const js::Value &v);
bool traverseObject(JS::HandleObject obj);
bool writeArrayBuffer(HandleObject obj);
bool writeTypedArray(HandleObject obj);
bool startObject(HandleObject obj, bool *backref);
bool startWrite(const Value &v);
bool traverseObject(HandleObject obj);
bool parseTransferable();
void reportErrorTransferable();
bool reportErrorTransferable();
bool transferOwnership();
inline void checkStack();
js::SCOutput &out;
SCOutput out;
// Vector of objects with properties remaining to be written.
//
// NB: These can span multiple compartments, so the compartment must be
// entered before any manipulation is performed.
js::AutoValueVector objs;
AutoValueVector objs;
// counts[i] is the number of properties of objs[i] remaining to be written.
// counts.length() == objs.length() and sum(counts) == ids.length().
js::Vector<size_t> counts;
Vector<size_t> counts;
// Ids of properties remaining to be written.
js::AutoIdVector ids;
AutoIdVector ids;
// The "memory" list described in the HTML5 internal structured cloning algorithm.
// memory is a superset of objs; items are never removed from Memory
// until a serialization operation is finished
typedef js::AutoObjectUnsigned32HashMap CloneMemory;
typedef AutoObjectUnsigned32HashMap CloneMemory;
CloneMemory memory;
// The user defined callbacks that will be used for cloning.
@ -297,10 +306,10 @@ struct JSStructuredCloneWriter {
void *closure;
// List of transferable objects
JS::RootedValue transferable;
JS::AutoObjectVector transferableObjects;
RootedValue transferable;
AutoObjectVector transferableObjects;
friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::HandleValue v);
friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, HandleValue v);
};
JS_FRIEND_API(uint64_t)
@ -314,16 +323,22 @@ JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
JS_STATIC_ASSERT(ScalarTypeDescr::TYPE_INT8 == 0);
namespace js {
static void
ReportErrorTransferable(JSContext *cx, const JSStructuredCloneCallbacks *callbacks)
{
if (callbacks && callbacks->reportError)
callbacks->reportError(cx, JS_SCERR_TRANSFERABLE);
else
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SC_NOT_TRANSFERABLE);
}
bool
WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp,
const JSStructuredCloneCallbacks *cb, void *cbClosure,
jsval transferable)
{
SCOutput out(cx);
JSStructuredCloneWriter w(out, cb, cbClosure, transferable);
return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp);
JSStructuredCloneWriter w(cx, cb, cbClosure, transferable);
return w.init() && w.write(v) && w.extractBuffer(bufp, nbytesp);
}
bool
@ -335,56 +350,62 @@ ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, MutableHandleV
return r.read(vp.address());
}
// This may acquire new ways of discarding transfer map entries as new
// Transferables are implemented.
// If the given buffer contains Transferables, free them. Note that custom
// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
// delete their transferables.
static void
DiscardEntry(uint32_t mapEntryDescriptor, const uint64_t *ptr)
Discard(uint64_t *buffer, size_t nbytes, const JSStructuredCloneCallbacks *cb, void *cbClosure)
{
if (mapEntryDescriptor == SCTAG_TM_ALLOC_DATA) {
uint64_t u = LittleEndian::readUint64(ptr);
js_free(reinterpret_cast<void*>(u));
} else {
JS_ASSERT(mapEntryDescriptor == SCTAG_TM_SHARED_BUFFER);
uint64_t u = LittleEndian::readUint64(ptr);
SharedArrayRawBuffer *raw = reinterpret_cast<SharedArrayRawBuffer *>(u);
if (raw)
raw->dropReference();
}
}
static void
Discard(const uint64_t *begin, const uint64_t *end)
{
const uint64_t *point = begin;
if (begin >= end)
JS_ASSERT(nbytes % sizeof(uint64_t) == 0);
if (nbytes < sizeof(uint64_t))
return; // Empty buffer
uint64_t u = LittleEndian::readUint64(point++);
uint32_t tag = uint32_t(u >> 32);
uint64_t *point = buffer;
uint32_t tag, data;
SCInput::getPair(point++, &tag, &data);
if (tag != SCTAG_TRANSFER_MAP_HEADER)
return;
if (TransferableMapHeader(u) == SCTAG_TM_TRANSFERRED)
if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
return;
// freeTransfer should not GC
JS::AutoAssertNoGC nogc;
uint64_t numTransferables = LittleEndian::readUint64(point++);
while (numTransferables--) {
uint64_t u = LittleEndian::readUint64(point++);
JS_ASSERT(uint32_t(u >> 32) == SCTAG_TRANSFER_MAP_ENTRY);
uint32_t mapEntryDescriptor = uint32_t(u);
if (mapEntryDescriptor >= SCTAG_TM_FIRST_OWNED) {
DiscardEntry(mapEntryDescriptor, point);
point += 2; // Pointer and userdata
uint32_t ownership;
SCInput::getPair(point++, &tag, &ownership);
JS_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
void *content;
SCInput::getPtr(point++, &content);
uint64_t extraData = LittleEndian::readUint64(point++);
if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
continue;
if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
js_free(content);
} else if (ownership == JS::SCTAG_TMO_SHARED_BUFFER) {
SharedArrayRawBuffer *raw = static_cast<SharedArrayRawBuffer*>(content);
if (raw)
raw->dropReference();
} else if (cb && cb->freeTransfer) {
cb->freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, cbClosure);
} else {
MOZ_ASSERT(false, "unknown ownership");
}
}
}
static void
ClearStructuredClone(const uint64_t *data, size_t nbytes)
ClearStructuredClone(uint64_t *data, size_t nbytes,
const JSStructuredCloneCallbacks *cb, void *cbClosure)
{
JS_ASSERT(nbytes % 8 == 0);
Discard(data, data + nbytes / 8);
js_free(const_cast<uint64_t*>(data));
Discard(data, nbytes, cb, cbClosure);
js_free(data);
}
bool
@ -402,40 +423,39 @@ StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *has
return true;
}
} /* namespace js */
static inline uint64_t
PairToUInt64(uint32_t tag, uint32_t data)
{
return uint64_t(data) | (uint64_t(tag) << 32);
}
bool
SCInput::eof()
{
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
return false;
}
namespace js {
SCInput::SCInput(JSContext *cx, uint64_t *data, size_t nbytes)
: cx(cx), point(data), end(data + nbytes / 8)
: cx(cx), point(data), bufEnd(data + nbytes / 8)
{
JS_ASSERT((uintptr_t(data) & 7) == 0);
// On 32-bit, we sometimes construct an SCInput from an SCOutput buffer,
// which is not guaranteed to be 8-byte aligned
JS_ASSERT((uintptr_t(data) & (sizeof(int) - 1)) == 0);
JS_ASSERT((nbytes & 7) == 0);
}
bool
SCInput::read(uint64_t *p)
{
if (point == end) {
if (point == bufEnd) {
*p = 0; /* initialize to shut GCC up */
return eof();
return reportTruncated();
}
*p = LittleEndian::readUint64(point++);
return true;
}
bool
SCInput::readNativeEndian(uint64_t *p)
{
if (point == bufEnd) {
*p = 0; /* initialize to shut GCC up */
return reportTruncated();
}
*p = *(point++);
return true;
}
bool
SCInput::readPair(uint32_t *tagp, uint32_t *datap)
{
@ -451,8 +471,8 @@ SCInput::readPair(uint32_t *tagp, uint32_t *datap)
bool
SCInput::get(uint64_t *p)
{
if (point == end)
return eof();
if (point == bufEnd)
return reportTruncated();
*p = LittleEndian::readUint64(point);
return true;
}
@ -460,7 +480,7 @@ SCInput::get(uint64_t *p)
bool
SCInput::getPair(uint32_t *tagp, uint32_t *datap)
{
uint64_t u;
uint64_t u = 0;
if (!get(&u))
return false;
@ -469,19 +489,12 @@ SCInput::getPair(uint32_t *tagp, uint32_t *datap)
return true;
}
bool
SCInput::replace(uint64_t u)
void
SCInput::getPair(const uint64_t *p, uint32_t *tagp, uint32_t *datap)
{
if (point == end)
return eof();
LittleEndian::writeUint64(point, u);
return true;
}
bool
SCInput::replacePair(uint32_t tag, uint32_t data)
{
return replace(PairToUInt64(tag, data));
uint64_t u = LittleEndian::readUint64(p);
*tagp = uint32_t(u >> 32);
*datap = uint32_t(u);
}
bool
@ -523,8 +536,8 @@ SCInput::readArray(T *p, size_t nelems)
* larger than the remaining data.
*/
size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point))
return eof();
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(bufEnd - point))
return reportTruncated();
copyAndSwapFromLittleEndian(p, point, nelems);
point += nwords;
@ -544,25 +557,26 @@ SCInput::readChars(jschar *p, size_t nchars)
return readArray((uint16_t *) p, nchars);
}
void
SCInput::getPtr(const uint64_t *p, void **ptr)
{
// No endianness conversion is used for pointers, since they are not sent
// across address spaces anyway.
*ptr = reinterpret_cast<void*>(*p);
}
bool
SCInput::readPtr(void **p)
{
// On a 32 bit system the void* variable we have to write to is only
// 32 bits, so we create a 64 temporary and discard the unused bits.
uint64_t tmp;
bool ret = read(&tmp);
*p = reinterpret_cast<void*>(tmp);
return ret;
uint64_t u;
if (!readNativeEndian(&u))
return false;
*p = reinterpret_cast<void*>(u);
return true;
}
SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {}
SCOutput::~SCOutput()
{
// Free any transferable data left lying around in the buffer
Discard(rawBuffer(), rawBuffer() + count());
}
bool
SCOutput::write(uint64_t u)
{
@ -685,8 +699,19 @@ SCOutput::extractBuffer(uint64_t **datap, size_t *sizep)
return (*datap = buf.extractRawBuffer()) != nullptr;
}
} /* namespace js */
JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
JSStructuredCloneWriter::~JSStructuredCloneWriter()
{
// Free any transferable data left lying around in the buffer
uint64_t *data;
size_t size;
MOZ_ALWAYS_TRUE(extractBuffer(&data, &size));
ClearStructuredClone(data, size, callbacks, closure);
}
bool
JSStructuredCloneWriter::parseTransferable()
{
@ -695,17 +720,13 @@ JSStructuredCloneWriter::parseTransferable()
if (JSVAL_IS_NULL(transferable) || JSVAL_IS_VOID(transferable))
return true;
if (!transferable.isObject()) {
reportErrorTransferable();
return false;
}
if (!transferable.isObject())
return reportErrorTransferable();
JSContext *cx = context();
RootedObject array(cx, &transferable.toObject());
if (!JS_IsArrayObject(cx, array)) {
reportErrorTransferable();
return false;
}
if (!JS_IsArrayObject(cx, array))
return reportErrorTransferable();
uint32_t length;
if (!JS_GetArrayLength(cx, array, &length)) {
@ -715,24 +736,18 @@ JSStructuredCloneWriter::parseTransferable()
RootedValue v(context());
for (uint32_t i = 0; i < length; ++i) {
if (!JS_GetElement(cx, array, i, &v)) {
if (!JS_GetElement(cx, array, i, &v))
return false;
}
if (!v.isObject()) {
reportErrorTransferable();
return false;
}
if (!v.isObject())
return reportErrorTransferable();
RootedObject tObj(context(), CheckedUnwrap(&v.toObject()));
JSObject* tObj = CheckedUnwrap(&v.toObject());
if (!tObj) {
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
return false;
}
if (!tObj->is<ArrayBufferObject>() && !tObj->is<SharedArrayBufferObject>()) {
reportErrorTransferable();
return false;
}
// No duplicates allowed
if (std::find(transferableObjects.begin(), transferableObjects.end(), tObj) != transferableObjects.end()) {
@ -747,13 +762,11 @@ JSStructuredCloneWriter::parseTransferable()
return true;
}
void
bool
JSStructuredCloneWriter::reportErrorTransferable()
{
if (callbacks && callbacks->reportError)
return callbacks->reportError(context(), JS_SCERR_TRANSFERABLE);
else
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_SC_NOT_TRANSFERABLE);
ReportErrorTransferable(context(), callbacks);
return false;
}
bool
@ -914,7 +927,7 @@ JSStructuredCloneWriter::startWrite(const Value &v)
// behind it. If we can, unwrap the object.
obj = CheckedUnwrap(obj);
if (!obj) {
JS_ReportError(context(), "Permission denied to access object");
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
return false;
}
@ -977,13 +990,11 @@ JSStructuredCloneWriter::writeTransferMap()
// Emit a placeholder pointer. We will steal the data and neuter the
// transferable later, in the case of ArrayBufferObject.
if (!out.writePair(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_UNFILLED))
if (!out.writePair(SCTAG_TRANSFER_MAP_PENDING_ENTRY, JS::SCTAG_TMO_UNFILLED))
return false;
if (!out.writePtr(nullptr)) // Pointer to ArrayBuffer contents or to SharedArrayRawBuffer.
return false;
if (!out.write(0)) // |byteLength| for an ArrayBuffer, 0 for SharedArrayBuffer
return false;
if (!out.write(0)) // |userdata|, intended to be passed to callbacks.
if (!out.write(0)) // extraData
return false;
}
@ -1008,38 +1019,52 @@ JSStructuredCloneWriter::transferOwnership()
for (JS::AutoObjectVector::Range tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
RootedObject obj(context(), tr.front());
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_ENTRY);
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point)) == SCTAG_TM_UNFILLED);
uint32_t tag;
JS::TransferableOwnership ownership;
void *content;
uint64_t extraData;
#if DEBUG
SCInput::getPair(point, &tag, (uint32_t*) &ownership);
MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
#endif
if (obj->is<ArrayBufferObject>()) {
size_t nbytes = obj->as<ArrayBufferObject>().byteLength();
void *contents = JS_StealArrayBufferContents(context(), obj);
if (!contents)
content = JS_StealArrayBufferContents(context(), obj);
if (!content)
return false; // Destructor will clean up the already-transferred data
uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_ALLOC_DATA);
LittleEndian::writeUint64(point++, entryTag);
LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(contents));
LittleEndian::writeUint64(point++, nbytes);
LittleEndian::writeUint64(point++, 0);
} else {
tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
ownership = JS::SCTAG_TMO_ALLOC_DATA;
extraData = nbytes;
} else if (obj->is<SharedArrayBufferObject>()) {
SharedArrayRawBuffer *rawbuf = obj->as<SharedArrayBufferObject>().rawBufferObject();
// Avoids a race condition where the parent thread frees the buffer
// before the child has accepted the transferable.
rawbuf->addReference();
uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_SHARED_BUFFER);
LittleEndian::writeUint64(point++, entryTag);
LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(rawbuf));
LittleEndian::writeUint64(point++, 0);
LittleEndian::writeUint64(point++, 0);
tag = SCTAG_TRANSFER_MAP_SHARED_BUFFER;
ownership = JS::SCTAG_TMO_SHARED_BUFFER;
content = rawbuf;
extraData = 0;
} else {
if (!callbacks || !callbacks->writeTransfer)
return reportErrorTransferable();
if (!callbacks->writeTransfer(context(), obj, closure, &tag, &ownership, &content, &extraData))
return false;
JS_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
}
LittleEndian::writeUint64(point++, PairToUInt64(tag, ownership));
LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(content));
LittleEndian::writeUint64(point++, extraData);
}
JS_ASSERT(point <= out.rawBuffer() + out.count());
JS_ASSERT_IF(point < out.rawBuffer() + out.count(),
uint32_t(LittleEndian::readUint64(point) >> 32) != SCTAG_TRANSFER_MAP_ENTRY);
uint32_t(LittleEndian::readUint64(point) >> 32) < SCTAG_TRANSFER_MAP_HEADER);
return true;
}
@ -1402,15 +1427,9 @@ JSStructuredCloneReader::startRead(Value *vp)
}
case SCTAG_TRANSFER_MAP_HEADER:
// A map header cannot be here but just at the beginning of the buffer.
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA,
"invalid input");
return false;
case SCTAG_TRANSFER_MAP_ENTRY:
// A map cannot be here but just at the beginning of the buffer.
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr,
case SCTAG_TRANSFER_MAP_PENDING_ENTRY:
// We should be past all the transfer map tags.
JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
JSMSG_SC_BAD_SERIALIZED_DATA,
"invalid input");
return false;
@ -1494,11 +1513,12 @@ JSStructuredCloneReader::readId(jsid *idp)
bool
JSStructuredCloneReader::readTransferMap()
{
JSContext *cx = context();
uint64_t *headerPos = in.tell();
uint32_t tag, data;
if (!in.getPair(&tag, &data))
return false;
return in.reportTruncated();
if (tag != SCTAG_TRANSFER_MAP_HEADER || TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
return true;
@ -1513,47 +1533,56 @@ JSStructuredCloneReader::readTransferMap()
if (!in.readPair(&tag, &data))
return false;
JS_ASSERT(tag == SCTAG_TRANSFER_MAP_ENTRY);
JS_ASSERT(data == SCTAG_TM_ALLOC_DATA || data == SCTAG_TM_SHARED_BUFFER);
JS_ASSERT(tag != SCTAG_TRANSFER_MAP_PENDING_ENTRY);
RootedObject obj(cx);
void *content;
if (!in.readPtr(&content))
return false;
uint64_t nbytes;
if (!in.read(&nbytes))
uint64_t extraData;
if (!in.read(&extraData))
return false;
uint64_t userdata;
if (!in.read(&userdata))
return false;
RootedObject obj(context());
if (data == SCTAG_TM_ALLOC_DATA)
obj = JS_NewArrayBufferWithContents(context(), nbytes, content);
else if (data == SCTAG_TM_SHARED_BUFFER)
if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) {
size_t nbytes = extraData;
JS_ASSERT(data == JS::SCTAG_TMO_ALLOC_DATA);
obj = JS_NewArrayBufferWithContents(cx, nbytes, content);
} else if (tag == SCTAG_TRANSFER_MAP_SHARED_BUFFER) {
JS_ASSERT(data == JS::SCTAG_TMO_SHARED_BUFFER);
obj = SharedArrayBufferObject::New(context(), (SharedArrayRawBuffer *)content);
} else {
if (!callbacks || !callbacks->readTransfer) {
ReportErrorTransferable(cx, callbacks);
return false;
}
if (!callbacks->readTransfer(cx, this, tag, content, extraData, closure, &obj))
return false;
MOZ_ASSERT(obj);
MOZ_ASSERT(!cx->isExceptionPending());
}
if (!obj)
return false;
// Rewind to the SCTAG_TRANSFER_MAP_ENTRY and mark this entry as unowned by
// the input buffer.
uint64_t *next = in.tell();
in.seek(pos);
MOZ_ALWAYS_TRUE(in.replacePair(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_UNOWNED));
in.seek(next);
// Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
// buffer.
*pos = PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED);
MOZ_ASSERT(headerPos < pos && pos < in.end());
if (!allObjs.append(ObjectValue(*obj)))
return false;
}
// Mark the whole transfer map as consumed
uint64_t *endPos = in.tell();
in.seek(headerPos);
MOZ_ALWAYS_TRUE(in.replacePair(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED));
in.seek(endPos);
// Mark the whole transfer map as consumed.
MOZ_ASSERT(headerPos <= in.tell());
#ifdef DEBUG
SCInput::getPair(headerPos, &tag, &data);
MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
MOZ_ASSERT(TransferableMapHeader(data) != SCTAG_TM_TRANSFERRED);
#endif
*headerPos = PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED);
return true;
}
@ -1587,11 +1616,12 @@ JSStructuredCloneReader::read(Value *vp)
return true;
}
using namespace js;
JS_PUBLIC_API(bool)
JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes,
uint32_t version, JS::MutableHandleValue vp,
uint32_t version, MutableHandleValue vp,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure)
{
@ -1610,9 +1640,9 @@ JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes,
}
JS_PUBLIC_API(bool)
JS_WriteStructuredClone(JSContext *cx, JS::HandleValue value, uint64_t **bufp, size_t *nbytesp,
JS_WriteStructuredClone(JSContext *cx, HandleValue value, uint64_t **bufp, size_t *nbytesp,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure, JS::HandleValue transferable)
void *closure, HandleValue transferable)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
@ -1626,9 +1656,11 @@ JS_WriteStructuredClone(JSContext *cx, JS::HandleValue value, uint64_t **bufp, s
}
JS_PUBLIC_API(bool)
JS_ClearStructuredClone(const uint64_t *data, size_t nbytes)
JS_ClearStructuredClone(uint64_t *data, size_t nbytes,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure)
{
ClearStructuredClone(data, nbytes);
ClearStructuredClone(data, nbytes, optionalCallbacks, closure);
return true;
}
@ -1645,7 +1677,7 @@ JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes,
}
JS_PUBLIC_API(bool)
JS_StructuredClone(JSContext *cx, JS::HandleValue value, JS::MutableHandleValue vp,
JS_StructuredClone(JSContext *cx, HandleValue value, MutableHandleValue vp,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure)
{
@ -1704,7 +1736,7 @@ void
JSAutoStructuredCloneBuffer::clear()
{
if (data_) {
ClearStructuredClone(data_, nbytes_);
ClearStructuredClone(data_, nbytes_, callbacks_, closure_);
data_ = nullptr;
nbytes_ = 0;
version_ = 0;
@ -1756,7 +1788,7 @@ JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *
}
bool
JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::MutableHandleValue vp,
JSAutoStructuredCloneBuffer::read(JSContext *cx, MutableHandleValue vp,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure)
{
@ -1767,17 +1799,17 @@ JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::MutableHandleValue vp,
}
bool
JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::HandleValue value,
JSAutoStructuredCloneBuffer::write(JSContext *cx, HandleValue value,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure)
{
JS::HandleValue transferable = JS::UndefinedHandleValue;
HandleValue transferable = UndefinedHandleValue;
return write(cx, value, transferable, optionalCallbacks, closure);
}
bool
JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::HandleValue value,
JS::HandleValue transferable,
JSAutoStructuredCloneBuffer::write(JSContext *cx, HandleValue value,
HandleValue transferable,
const JSStructuredCloneCallbacks *optionalCallbacks,
void *closure)
{
@ -1812,7 +1844,7 @@ JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len)
}
JS_PUBLIC_API(bool)
JS_ReadTypedArray(JSStructuredCloneReader *r, JS::MutableHandleValue vp)
JS_ReadTypedArray(JSStructuredCloneReader *r, MutableHandleValue vp)
{
uint32_t tag, nelems;
if (!r->input().readPair(&tag, &nelems))
@ -1844,7 +1876,7 @@ JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len)
}
JS_PUBLIC_API(bool)
JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::HandleValue v)
JS_WriteTypedArray(JSStructuredCloneWriter *w, HandleValue v)
{
JS_ASSERT(v.isObject());
assertSameCompartment(w->context(), v);
@ -1855,7 +1887,7 @@ JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::HandleValue v)
if (obj->is<WrapperObject>())
obj = CheckedUnwrap(obj);
if (!obj) {
JS_ReportError(w->context(), "Permission denied to access object");
JS_ReportErrorNumber(w->context(), js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
return false;
}
return w->writeTypedArray(obj);

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

@ -435,6 +435,9 @@ CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer,
static const JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = {
CloneNonReflectorsRead,
CloneNonReflectorsWrite,
nullptr,
nullptr,
nullptr,
nullptr
};

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

@ -49,6 +49,14 @@ template <class TargetUnits>
gfx::IntSizeTyped<TargetUnits> ViewAs(const nsIntSize& aSize) {
return gfx::IntSizeTyped<TargetUnits>(aSize.width, aSize.height);
}
template <class TargetUnits>
gfx::IntPointTyped<TargetUnits> ViewAs(const nsIntPoint& aPoint) {
return gfx::IntPointTyped<TargetUnits>(aPoint.x, aPoint.y);
}
template <class TargetUnits>
gfx::IntRectTyped<TargetUnits> ViewAs(const nsIntRect& aRect) {
return gfx::IntRectTyped<TargetUnits>(aRect.x, aRect.y, aRect.width, aRect.height);
}
// Convenience functions for casting typed entities to untyped entities.
// Using these functions does not require a justification, but once we convert

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

@ -642,30 +642,37 @@ CalculateRootCompositionSize(FrameMetrics& aMetrics,
}
nsIPresShell* rootPresShell = nullptr;
if (rootPresContext) {
// See the comments in the code that calculates the root
// composition bounds in RecordFrameMetrics.
// TODO: Reuse that code here.
nsIPresShell* rootPresShell = rootPresContext->PresShell();
if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) {
if (nsView* view = rootFrame->GetView()) {
nsIWidget* widget = view->GetWidget();
#ifdef MOZ_WIDGET_ANDROID
// Android hack - temporary workaround for bug 983208 until we figure
// out what a proper fix is.
if (!widget) {
widget = rootFrame->GetNearestWidget();
}
#endif
LayoutDeviceToParentLayerScale parentResolution(
rootPresShell->GetCumulativeResolution().width
/ rootPresShell->GetResolution().width);
int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel();
nsRect viewBounds = view->GetBounds();
LayerSize viewSize = ViewAs<LayerPixel>(
(LayoutDeviceRect::FromAppUnits(viewBounds, rootAUPerDevPixel)
* parentResolution).Size(), PixelCastJustification::ParentLayerToLayerForRootComposition);
nsIWidget* widget =
#ifdef MOZ_WIDGET_ANDROID
rootFrame->GetNearestWidget();
#else
view->GetWidget();
#endif
if (widget) {
nsIntRect bounds;
widget->GetBounds(bounds);
rootCompositionSize = LayerSize(ViewAs<LayerPixel>(bounds.Size()));
nsIntRect widgetBounds;
widget->GetBounds(widgetBounds);
rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size()));
#ifdef MOZ_WIDGET_ANDROID
if (viewSize.height < rootCompositionSize.height) {
rootCompositionSize.height = viewSize.height;
}
#endif
} else {
LayoutDeviceToParentLayerScale parentResolution(
rootPresShell->GetCumulativeResolution().width
/ rootPresShell->GetResolution().width);
int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel();
nsRect viewBounds = view->GetBounds();
rootCompositionSize = ViewAs<LayerPixel>(
(LayoutDeviceRect::FromAppUnits(viewBounds, rootAUPerDevPixel)
* parentResolution).Size(), PixelCastJustification::ParentLayerToLayerForRootComposition);
rootCompositionSize = viewSize;
}
}
}
@ -793,7 +800,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),
frameForCompositionBoundsCalculation->GetSize());
metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel)
* metrics.GetParentResolution());
* metrics.GetParentResolution());
// For the root scroll frame of the root content document, the above calculation
// will yield the size of the viewport frame as the composition bounds, which
@ -808,23 +815,35 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
if (isRootContentDocRootScrollFrame) {
if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
if (nsView* view = rootFrame->GetView()) {
nsIWidget* widget = view->GetWidget();
nsRect viewBoundsAppUnits = view->GetBounds() + rootFrame->GetOffsetToCrossDoc(aReferenceFrame);
ParentLayerIntRect viewBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(viewBoundsAppUnits, auPerDevPixel)
* metrics.GetParentResolution());
// On Android, we need to do things a bit differently to get things
// right (see bug 983208, bug 988882). We use the bounds of the nearest
// widget, but clamp the height to the view bounds height. This clamping
// is done to get correct results for a page where the page is sized to
// the screen and thus the dynamic toolbar never disappears. In such a
// case, we want the composition bounds to exclude the toolbar height,
// but the widget bounds includes it. We don't currently have a good way
// of knowing about the toolbar height, but clamping to the view bounds
// height gives the correct answer in the cases we care about.
nsIWidget* widget =
#ifdef MOZ_WIDGET_ANDROID
// Android hack - temporary workaround for bug 983208 until we figure
// out what a proper fix is.
if (!widget) {
widget = rootFrame->GetNearestWidget();
}
rootFrame->GetNearestWidget();
#else
view->GetWidget();
#endif
if (widget) {
nsIntRect bounds;
widget->GetBounds(bounds);
metrics.mCompositionBounds = ParentLayerIntRect::FromUnknownRect(mozilla::gfx::IntRect(
bounds.x, bounds.y, bounds.width, bounds.height));
nsIntRect widgetBounds;
widget->GetBounds(widgetBounds);
metrics.mCompositionBounds = ViewAs<ParentLayerPixel>(widgetBounds);
#ifdef MOZ_WIDGET_ANDROID
if (viewBounds.height < metrics.mCompositionBounds.height) {
metrics.mCompositionBounds.height = viewBounds.height;
}
#endif
} else {
nsRect viewBounds = view->GetBounds() + rootFrame->GetOffsetToCrossDoc(aReferenceFrame);
metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(viewBounds, auPerDevPixel)
* metrics.GetParentResolution());
metrics.mCompositionBounds = viewBounds;
}
}
}

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

@ -3676,6 +3676,51 @@ nsLayoutUtils::ComputeHeightDependentValue(
return 0;
}
/* static */ void
nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot)
{
nsAutoTArray<nsIFrame*, 4> subtrees;
subtrees.AppendElement(aSubtreeRoot);
// dirty descendants, iterating over subtrees that may include
// additional subtrees associated with placeholders
do {
nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
subtrees.RemoveElementAt(subtrees.Length() - 1);
// Mark all descendants dirty (using an nsTArray stack rather than
// recursion).
// Note that nsHTMLReflowState::InitResizeFlags has some similar
// code; see comments there for how and why it differs.
nsAutoTArray<nsIFrame*, 32> stack;
stack.AppendElement(subtreeRoot);
do {
nsIFrame *f = stack.ElementAt(stack.Length() - 1);
stack.RemoveElementAt(stack.Length() - 1);
f->MarkIntrinsicWidthsDirty();
if (f->GetType() == nsGkAtoms::placeholderFrame) {
nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
// We have another distinct subtree we need to mark.
subtrees.AppendElement(oof);
}
}
nsIFrame::ChildListIterator lists(f);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* kid = childFrames.get();
stack.AppendElement(kid);
}
}
} while (stack.Length() != 0);
} while (subtrees.Length() != 0);
}
#define MULDIV(a,b,c) (nscoord(int64_t(a) * int64_t(b) / int64_t(c)))
/* static */ nsSize
@ -6125,36 +6170,34 @@ nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame)
nsPresContext* presContext = aFrame->PresContext();
nsIPresShell* presShell = presContext->PresShell();
// For the root scroll frame of the root content document, the above calculation
// will yield the size of the viewport frame as the composition bounds, which
// doesn't actually correspond to what is visible when
// nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of
// the prescontext that the viewport frame is reflowed into. In that case if our
// document has a widget then the widget's bounds will correspond to what is
// visible. If we don't have a widget the root view's bounds correspond to what
// would be visible because they don't get modified by setCSSViewport.
// See the comments in the code that calculates the root
// composition bounds in RecordFrameMetrics.
// TODO: Reuse that code here.
bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
&& aFrame == presShell->GetRootScrollFrame();
if (isRootContentDocRootScrollFrame) {
if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
if (nsView* view = rootFrame->GetView()) {
nsIWidget* widget = view->GetWidget();
nsSize viewSize = view->GetBounds().Size();
nsIWidget* widget =
#ifdef MOZ_WIDGET_ANDROID
// Android hack - temporary workaround for bug 983208 until we figure
// out what a proper fix is.
if (!widget) {
widget = rootFrame->GetNearestWidget();
}
rootFrame->GetNearestWidget();
#else
view->GetWidget();
#endif
if (widget) {
nsIntRect bounds;
widget->GetBounds(bounds);
nsIntRect widgetBounds;
widget->GetBounds(widgetBounds);
int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
size = nsSize(bounds.width * auPerDevPixel,
bounds.height * auPerDevPixel);
size = nsSize(widgetBounds.width * auPerDevPixel,
widgetBounds.height * auPerDevPixel);
#ifdef MOZ_WIDGET_ANDROID
if (viewSize.height < size.height) {
size.height = viewSize.height;
}
#endif
} else {
nsRect viewBounds = view->GetBounds();
size = nsSize(viewBounds.width, viewBounds.height);
size = viewSize;
}
}
}

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

@ -1210,6 +1210,8 @@ public:
nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) == 0);
}
static void MarkDescendantsDirty(nsIFrame *aSubtreeRoot);
/*
* Calculate the used values for 'width' and 'height' for a replaced element.
*

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

@ -5268,9 +5268,9 @@ nsresult PresShell::SetResolution(float aXResolution, float aYResolution)
gfxSize PresShell::GetCumulativeResolution()
{
gfxSize resolution = GetResolution();
nsCOMPtr<nsIPresShell> parent = GetParentPresShell();
if (parent) {
resolution = resolution * parent->GetCumulativeResolution();
nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
if (parentCtx) {
resolution = resolution * parentCtx->PresShell()->GetCumulativeResolution();
}
return resolution;
}
@ -6162,13 +6162,13 @@ PresShell::GetRootWindow()
// If we don't have DOM window, we're zombie, we should find the root window
// with our parent shell.
nsCOMPtr<nsIPresShell> parent = GetParentPresShell();
nsCOMPtr<nsIPresShell> parent = GetParentPresShellForEventHandling();
NS_ENSURE_TRUE(parent, nullptr);
return parent->GetRootWindow();
}
already_AddRefed<nsIPresShell>
PresShell::GetParentPresShell()
PresShell::GetParentPresShellForEventHandling()
{
NS_ENSURE_TRUE(mPresContext, nullptr);
@ -6200,7 +6200,7 @@ PresShell::RetargetEventToParent(WidgetGUIEvent* aEvent,
// That way at least the UI key bindings can work.
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShell();
nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShellForEventHandling();
NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
// Fake the event as though it's from the parent pres shell's root frame.

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

@ -629,7 +629,7 @@ protected:
nsIDocument* GetTouchEventTargetDocument();
#endif
bool InZombieDocument(nsIContent *aContent);
already_AddRefed<nsIPresShell> GetParentPresShell();
already_AddRefed<nsIPresShell> GetParentPresShellForEventHandling();
nsIContent* GetCurrentEventContent();
nsIFrame* GetCurrentEventFrame();
nsresult RetargetEventToParent(mozilla::WidgetGUIEvent* aEvent,

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

@ -85,6 +85,7 @@ nsFirstLetterFrame::SetInitialChildList(ChildListID aListID,
for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) {
NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent");
restyleManager->ReparentStyleContext(e.get());
nsLayoutUtils::MarkDescendantsDirty(e.get());
}
mFrames.SetFrames(aChildList);
@ -316,6 +317,7 @@ nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresCont
nsRefPtr<nsStyleContext> newSC;
newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC);
continuation->SetStyleContext(newSC);
nsLayoutUtils::MarkDescendantsDirty(continuation);
}
//XXX Bidi may not be involved but we have to use the list name
@ -369,6 +371,7 @@ nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
mStyleContext;
sc = aPresContext->StyleSet()->ResolveStyleForNonElement(parentSC);
kid->SetStyleContext(sc);
nsLayoutUtils::MarkDescendantsDirty(kid);
}
}
}

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

@ -274,9 +274,18 @@ public:
nsSize(aCrossSize, aMainSize);
}
// Are my axes reversed with respect to what the author asked for?
// (We may reverse the axes in the FlexboxAxisTracker constructor and set
// this flag, to avoid reflowing our children in bottom-to-top order.)
bool AreAxesInternallyReversed() const
{
return mAreAxesInternallyReversed;
}
private:
AxisOrientationType mMainAxis;
AxisOrientationType mCrossAxis;
bool mAreAxesInternallyReversed;
};
/**
@ -331,10 +340,11 @@ public:
// Returns the distance between this FlexItem's baseline and the cross-start
// edge of its margin-box. Used in baseline alignment.
// (This function needs to be told what the cross axis is so that it can
// look up the appropriate component from mMargin.)
nscoord GetBaselineOffsetFromOuterCrossStart(
AxisOrientationType aCrossAxis) const;
// (This function needs to be told what cross axis is & which edge we're
// measuring the baseline from, so that it can look up the appropriate
// components from mMargin.)
nscoord GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis,
AxisEdgeType aEdge) const;
float GetShareOfFlexWeightSoFar() const { return mShareOfFlexWeightSoFar; }
@ -592,7 +602,7 @@ public:
mTotalInnerHypotheticalMainSize(0),
mTotalOuterHypotheticalMainSize(0),
mLineCrossSize(0),
mBaselineOffsetFromCrossStart(nscoord_MIN)
mBaselineOffset(nscoord_MIN)
{}
// Returns the sum of our FlexItems' outer hypothetical main sizes.
@ -630,16 +640,20 @@ public:
return mNumItems;
}
// Adds the given FlexItem to our list of items, and adds its hypothetical
// Adds the given FlexItem to our list of items (at the front or back
// depending on aShouldInsertAtFront), and adds its hypothetical
// outer & inner main sizes to our totals. Use this method instead of
// directly modifying the item list, so that our bookkeeping remains correct.
// XXXdholbert Bug 983427 will extend this to let the caller choose whether
// the new FlexItem goes at the front or the back of the list.
void AddItem(FlexItem* aItem,
bool aShouldInsertAtFront,
nscoord aItemInnerHypotheticalMainSize,
nscoord aItemOuterHypotheticalMainSize)
{
mItems.insertBack(aItem);
if (aShouldInsertAtFront) {
mItems.insertFront(aItem);
} else {
mItems.insertBack(aItem);
}
mNumItems++;
mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize;
mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize;
@ -659,11 +673,17 @@ public:
mLineCrossSize = aLineCrossSize;
}
// Returns the distance from the cross-start edge of this FlexLine
// to its baseline (derived from its baseline-aligned FlexItems).
// If there are no baseline-aligned FlexItems, returns nscoord_MIN.
nscoord GetBaselineOffsetFromCrossStart() const {
return mBaselineOffsetFromCrossStart;
/**
* Returns the offset within this line where any baseline-aligned FlexItems
* should place their baseline. Usually, this represents a distance from the
* line's cross-start edge, but if we're internally reversing the axes (see
* AreAxesInternallyReversed()), this instead represents the distance from
* its cross-end edge.
*
* If there are no baseline-aligned FlexItems, returns nscoord_MIN.
*/
nscoord GetBaselineOffset() const {
return mBaselineOffset;
}
// Runs the "resolve the flexible lengths" algorithm, distributing
@ -695,7 +715,7 @@ private:
nscoord mTotalInnerHypotheticalMainSize;
nscoord mTotalOuterHypotheticalMainSize;
nscoord mLineCrossSize;
nscoord mBaselineOffsetFromCrossStart;
nscoord mBaselineOffset;
};
// Information about a strut left behind by a FlexItem that's been collapsed
@ -1205,8 +1225,8 @@ FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize)
}
nscoord
FlexItem::GetBaselineOffsetFromOuterCrossStart(
AxisOrientationType aCrossAxis) const
FlexItem::GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis,
AxisEdgeType aEdge) const
{
// NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
// measurement -- it's the distance from the border-top edge of this FlexItem
@ -1217,18 +1237,23 @@ FlexItem::GetBaselineOffsetFromOuterCrossStart(
"Only expecting to be doing baseline computations when the "
"cross axis is vertical");
Side sideToMeasureFrom = kAxisOrientationToSidesMap[aCrossAxis][aEdge];
nscoord marginTopToBaseline = mAscent + mMargin.top;
if (aCrossAxis == eAxis_TB) {
// Top-to-bottom (normal case): the distance from the cross-start margin-box
// edge (i.e. the margin-top edge) to the baseline is ascent + margin-top.
if (sideToMeasureFrom == eSideTop) {
// Measuring from top (normal case): the distance from the margin-box top
// edge to the baseline is just ascent + margin-top.
return marginTopToBaseline;
}
// Bottom-to-top: The distance from the cross-start margin-box edge (i.e. the
// margin-bottom edge) to the baseline is just the margin-box cross size
// (i.e. outer cross size), minus the distance from margin-top to baseline
// (already computed above).
MOZ_ASSERT(sideToMeasureFrom == eSideBottom,
"We already checked that we're dealing with a vertical axis, and "
"we're not using the top side, so that only leaves the bottom...");
// Measuring from bottom: The distance from the margin-box bottom edge to the
// baseline is just the margin-box cross size (i.e. outer cross size), minus
// the already-computed distance from margin-top to baseline.
return GetOuterCrossSize(aCrossAxis) - marginTopToBaseline;
}
@ -1392,7 +1417,8 @@ public:
FlexItem& aItem);
void EnterAlignPackingSpace(const FlexLine& aLine,
const FlexItem& aItem);
const FlexItem& aItem,
const FlexboxAxisTracker& aAxisTracker);
// Resets our position to the cross-start edge of this line.
inline void ResetPosition() { mPosition = 0; }
@ -1817,6 +1843,16 @@ MainAxisPositionTracker::
}
}
// If our main axis is (internally) reversed, swap the justify-content
// "flex-start" and "flex-end" behaviors:
if (aAxisTracker.AreAxesInternallyReversed()) {
if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_START) {
mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_END;
} else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_END) {
mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
}
}
// Figure out how much space we'll set aside for auto margins or
// packing spaces, and advance past any leading packing-space.
if (mNumAutoMarginsInMainAxis == 0 &&
@ -1975,6 +2011,16 @@ CrossAxisPositionTracker::
}
}
// If our cross axis is (internally) reversed, swap the align-content
// "flex-start" and "flex-end" behaviors:
if (aAxisTracker.AreAxesInternallyReversed()) {
if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_START) {
mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_END;
} else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_END) {
mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START;
}
}
// Figure out how much space we'll set aside for packing spaces, and advance
// past any leading packing-space.
if (mPackingSpaceRemaining != 0) {
@ -2116,7 +2162,8 @@ FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
// crossEndToBaseline.
nscoord crossStartToBaseline =
item->GetBaselineOffsetFromOuterCrossStart(aAxisTracker.GetCrossAxis());
item->GetBaselineOffsetFromOuterCrossEdge(aAxisTracker.GetCrossAxis(),
eAxisEdge_Start);
nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
// Now, update our "largest" values for these (across all the flex items
@ -2131,10 +2178,12 @@ FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
}
}
// The line's baseline is the distance from the cross-start edge to the
// furthest baseline. (The item(s) with that baseline will be exactly
// aligned with the line's cross-start edge.)
mBaselineOffsetFromCrossStart = crossStartToFurthestBaseline;
// The line's baseline offset is the distance from the line's edge (start or
// end, depending on whether we've flipped the axes) to the furthest
// item-baseline. The item(s) with that baseline will be exactly aligned with
// the line's edge.
mBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
crossEndToFurthestBaseline : crossStartToFurthestBaseline;
// The line's cross-size is the larger of:
// (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
@ -2221,7 +2270,8 @@ SingleLineCrossAxisPositionTracker::
void
SingleLineCrossAxisPositionTracker::
EnterAlignPackingSpace(const FlexLine& aLine,
const FlexItem& aItem)
const FlexItem& aItem,
const FlexboxAxisTracker& aAxisTracker)
{
// We don't do align-self alignment on items that have auto margins
// in the cross axis.
@ -2229,12 +2279,26 @@ SingleLineCrossAxisPositionTracker::
return;
}
switch (aItem.GetAlignSelf()) {
uint8_t alignSelf = aItem.GetAlignSelf();
// NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
// auto-sized items (which we've already done).
if (alignSelf == NS_STYLE_ALIGN_ITEMS_STRETCH) {
alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
}
// If our cross axis is (internally) reversed, swap the align-self
// "flex-start" and "flex-end" behaviors:
if (aAxisTracker.AreAxesInternallyReversed()) {
if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_START) {
alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_END;
} else if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_END) {
alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
}
}
switch (alignSelf) {
case NS_STYLE_ALIGN_ITEMS_FLEX_START:
case NS_STYLE_ALIGN_ITEMS_STRETCH:
// No space to skip over -- we're done.
// NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
// auto-sized items (which we've already done).
break;
case NS_STYLE_ALIGN_ITEMS_FLEX_END:
mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
@ -2245,15 +2309,33 @@ SingleLineCrossAxisPositionTracker::
(aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
break;
case NS_STYLE_ALIGN_ITEMS_BASELINE: {
nscoord lineBaselineOffset =
aLine.GetBaselineOffsetFromCrossStart();
// Normally, baseline-aligned items are collectively aligned with the
// line's cross-start edge; however, if our cross axis is (internally)
// reversed, we instead align them with the cross-end edge.
nscoord itemBaselineOffset =
aItem.GetBaselineOffsetFromOuterCrossStart(mAxis);
MOZ_ASSERT(lineBaselineOffset >= itemBaselineOffset,
"failed at finding largest baseline offset");
aItem.GetBaselineOffsetFromOuterCrossEdge(mAxis,
aAxisTracker.AreAxesInternallyReversed() ?
eAxisEdge_End : eAxisEdge_Start);
// Advance so that aItem's baseline is aligned with the line's baseline.
mPosition += (lineBaselineOffset - itemBaselineOffset);
nscoord lineBaselineOffset = aLine.GetBaselineOffset();
NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
"failed at finding largest baseline offset");
// How much do we need to adjust our position (from the line edge),
// to get the item's baseline to hit the line's baseline offset:
nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
if (aAxisTracker.AreAxesInternallyReversed()) {
// Advance to align item w/ line's flex-end edge (as in FLEX_END case):
mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
// ...and step *back* by the baseline adjustment:
mPosition -= baselineDiff;
} else {
// mPosition is already at line's flex-start edge.
// From there, we step *forward* by the baseline adjustment:
mPosition += baselineDiff;
}
break;
}
default:
@ -2262,7 +2344,9 @@ SingleLineCrossAxisPositionTracker::
}
}
FlexboxAxisTracker::FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame)
FlexboxAxisTracker::FlexboxAxisTracker(
nsFlexContainerFrame* aFlexContainerFrame)
: mAreAxesInternallyReversed(false)
{
const nsStylePosition* pos = aFlexContainerFrame->StylePosition();
uint32_t flexDirection = pos->mFlexDirection;
@ -2324,19 +2408,43 @@ FlexboxAxisTracker::FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame
mCrossAxis = GetReverseAxis(mCrossAxis);
}
// Master switch to enable/disable bug 983427's code for reversing our axes
// and reversing some logic, to avoid reflowing children in bottom-to-top
// order. (This switch can be removed eventually, but for now, it allows
// this special-case code path to be compared against the normal code path.)
//
// XXXdholbert Initially, I'm defaulting this switch to *off*, meaning the
// new code isn't enabled yet. A subsequent patch will turn it on, once
// intermediate patches have made us react to AreAxesInternallyReversed()
// correctly in the appropriate places.
static bool sPreventBottomToTopChildOrdering = false;
if (sPreventBottomToTopChildOrdering) {
// If either axis is bottom-to-top, we flip both axes (and set a flag
// so that we can flip some logic to make the reversal transparent).
if (eAxis_BT == mMainAxis || eAxis_BT == mCrossAxis) {
mMainAxis = GetReverseAxis(mMainAxis);
mCrossAxis = GetReverseAxis(mCrossAxis);
mAreAxesInternallyReversed = true;
}
}
MOZ_ASSERT(IsAxisHorizontal(mMainAxis) != IsAxisHorizontal(mCrossAxis),
"main & cross axes should be in different dimensions");
}
// Allocates a new FlexLine, adds it at the back of the given LinkedList,
// and returs a pointer to it.
// XXXdholbert Bug 983427 will extend this to let the caller choose whether
// the new FlexLine goes at the front or the back of the list.
// Allocates a new FlexLine, adds it to the given LinkedList (at the front or
// back depending on aShouldInsertAtFront), and returns a pointer to it.
static FlexLine*
AddNewFlexLineToList(LinkedList<FlexLine>& aLines)
AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
bool aShouldInsertAtFront)
{
FlexLine* newLine = new FlexLine();
aLines.insertBack(newLine);
if (aShouldInsertAtFront) {
aLines.insertFront(newLine);
} else {
aLines.insertBack(newLine);
}
return newLine;
}
@ -2355,9 +2463,17 @@ nsFlexContainerFrame::GenerateFlexLines(
const bool isSingleLine =
NS_STYLE_FLEX_WRAP_NOWRAP == aReflowState.mStylePosition->mFlexWrap;
// If we're transparently reversing axes, then we'll need to link up our
// FlexItems and FlexLines in the reverse order, so that the rest of flex
// layout (with flipped axes) will still produce the correct result.
// Here, we declare a convenience bool that we'll pass when adding a new
// FlexLine or FlexItem, to make us insert it at the beginning of its list
// (so the list ends up reversed).
const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed();
// We have at least one FlexLine. Even an empty flex container has a single
// (empty) flex line.
FlexLine* curLine = AddNewFlexLineToList(aLines);
FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
nscoord wrapThreshold;
if (isSingleLine) {
@ -2401,7 +2517,7 @@ nsFlexContainerFrame::GenerateFlexLines(
// Honor "page-break-before", if we're multi-line and this line isn't empty:
if (!isSingleLine && !curLine->IsEmpty() &&
childFrame->StyleDisplay()->mBreakBefore) {
curLine = AddNewFlexLineToList(aLines);
curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
}
nsAutoPtr<FlexItem> item;
@ -2431,19 +2547,19 @@ nsFlexContainerFrame::GenerateFlexLines(
!curLine->IsEmpty() && // No need to wrap at start of a line.
wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
itemOuterHypotheticalMainSize)) {
curLine = AddNewFlexLineToList(aLines);
curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
}
// Add item to current flex line (and update the line's bookkeeping about
// how large its items collectively are).
curLine->AddItem(item.forget(),
curLine->AddItem(item.forget(), shouldInsertAtFront,
itemInnerHypotheticalMainSize,
itemOuterHypotheticalMainSize);
// Honor "page-break-after", if we're multi-line and have more children:
if (!isSingleLine && childFrame->GetNextSibling() &&
childFrame->StyleDisplay()->mBreakAfter) {
curLine = AddNewFlexLineToList(aLines);
curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
}
itemIdxInContainer++;
}
@ -2636,6 +2752,23 @@ ResolveReflowedChildAscent(nsIFrame* aFrame,
}
}
/**
* Given the flex container's "logical ascent" (i.e. distance from the
* flex container's content-box cross-start edge to its baseline), returns
* its actual physical ascent value (the distance from the *border-box* top
* edge to its baseline).
*/
static nscoord
ComputePhysicalAscentFromLogicalAscent(nscoord aLogicalAscent,
nscoord aContentBoxCrossSize,
const nsHTMLReflowState& aReflowState,
const FlexboxAxisTracker& aAxisTracker)
{
return aReflowState.ComputedPhysicalBorderPadding().top +
PhysicalPosFromLogicalPos(aLogicalAscent, aContentBoxCrossSize,
aAxisTracker.GetCrossAxis());
}
nsresult
nsFlexContainerFrame::SizeItemInCrossAxis(
nsPresContext* aPresContext,
@ -2741,7 +2874,7 @@ FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition,
nscoord itemCrossBorderBoxSize =
item->GetCrossSize() +
item->GetBorderPaddingSizeInAxis(aAxisTracker.GetCrossAxis());
lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item);
lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item, aAxisTracker);
lineCrossAxisPosnTracker.EnterMargin(item->GetMargin());
lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize);
@ -2949,29 +3082,23 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
}
}
// Set the flex container's baseline, from the baseline-alignment position
// of the first line's baseline-aligned items.
// If the container should derive its baseline from the first FlexLine,
// do that here (while crossAxisPosnTracker is conveniently pointing
// at the cross-start edge of that line, which the line's baseline offset is
// measured from):
nscoord flexContainerAscent;
nscoord firstLineBaselineOffset =
lines.getFirst()->GetBaselineOffsetFromCrossStart();
if (firstLineBaselineOffset == nscoord_MIN) {
// No baseline-aligned flex items in first line --> just use a sentinel
// value for now, and we'll update it during final reflow.
flexContainerAscent = nscoord_MIN;
} else {
// Add the position of the first line to that line's baseline-alignment
// offset, to get the baseline offset with respect to the *container's*
// cross-start edge.
nscoord firstLineBaselineOffsetWRTContainer =
firstLineBaselineOffset + crossAxisPosnTracker.GetPosition();
// The container's ascent is that ^ offset, converted out of logical coords
// (into distance from top of content-box), plus the top border/padding
// (since ascent is measured with respect to the top of the border-box).
flexContainerAscent = aReflowState.ComputedPhysicalBorderPadding().top +
PhysicalPosFromLogicalPos(firstLineBaselineOffsetWRTContainer,
contentBoxCrossSize,
aAxisTracker.GetCrossAxis());
if (!aAxisTracker.AreAxesInternallyReversed()) {
nscoord firstLineBaselineOffset = lines.getFirst()->GetBaselineOffset();
if (firstLineBaselineOffset == nscoord_MIN) {
// No baseline-aligned items in line. Use sentinel value to prompt us to
// get baseline from the first FlexItem after we've reflowed it.
flexContainerAscent = nscoord_MIN;
} else {
flexContainerAscent =
ComputePhysicalAscentFromLogicalAscent(
crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
contentBoxCrossSize, aReflowState, aAxisTracker);
}
}
for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
@ -2990,6 +3117,24 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
crossAxisPosnTracker.TraversePackingSpace();
}
// If the container should derive its baseline from the last FlexLine,
// do that here (while crossAxisPosnTracker is conveniently pointing
// at the cross-end edge of that line, which the line's baseline offset is
// measured from):
if (aAxisTracker.AreAxesInternallyReversed()) {
nscoord lastLineBaselineOffset = lines.getLast()->GetBaselineOffset();
if (lastLineBaselineOffset == nscoord_MIN) {
// No baseline-aligned items in line. Use sentinel value to prompt us to
// get baseline from the last FlexItem after we've reflowed it.
flexContainerAscent = nscoord_MIN;
} else {
flexContainerAscent =
ComputePhysicalAscentFromLogicalAscent(
crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset,
contentBoxCrossSize, aReflowState, aAxisTracker);
}
}
// Before giving each child a final reflow, calculate the origin of the
// flex container's content box (with respect to its border-box), so that
// we can compute our flex item's final positions.

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

@ -286,6 +286,7 @@ ReparentChildListStyle(nsPresContext* aPresContext,
for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
NS_ASSERTION(e.get()->GetParent() == aParentFrame, "Bogus parentage");
restyleManager->ReparentStyleContext(e.get());
nsLayoutUtils::MarkDescendantsDirty(e.get());
}
}
@ -424,6 +425,7 @@ nsInlineFrame::DrainSelfOverflowListInternal(DrainFlags aFlags,
f->SetParent(this);
if (inFirstLine) {
restyleManager->ReparentStyleContext(f);
nsLayoutUtils::MarkDescendantsDirty(f);
}
}
}
@ -529,6 +531,7 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext,
child->SetParent(this);
if (inFirstLine) {
restyleManager->ReparentStyleContext(child);
nsLayoutUtils::MarkDescendantsDirty(child);
}
// We also need to do the same for |frame|'s next-in-flows that are in
// the sibling list. Otherwise, if we reflow |frame| and it's complete
@ -563,6 +566,7 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext,
nextInFlow->SetParent(this);
if (inFirstLine) {
restyleManager->ReparentStyleContext(nextInFlow);
nsLayoutUtils::MarkDescendantsDirty(nextInFlow);
}
}
else {
@ -1005,6 +1009,7 @@ nsFirstLineFrame::PullOneFrame(nsPresContext* aPresContext, InlineReflowState& i
// style-context that we just pulled.
NS_ASSERTION(frame->GetParent() == this, "Incorrect parent?");
aPresContext->RestyleManager()->ReparentStyleContext(frame);
nsLayoutUtils::MarkDescendantsDirty(frame);
}
return frame;
}

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

@ -4441,7 +4441,6 @@ nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
nsTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{
nsFrame::DidSetStyleContext(aOldStyleContext);
ClearTextRuns();
}
class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry

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

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Testing first-letter handling of font/text styles</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html { overflow: hidden; }
body {
margin: 20px;
font-size: 600%;
line-height: 1.2em;
font-family: serif;
}
div { width: 5em; }
p { width: 0.5em; margin: 0; font-style: italic; }
span.boldnormal { font-weight: bold; font-style: normal; }
p.floatingfirst::first-letter { float: left; }
</style>
</head>
<body>
<div>
<p class="floatingfirst"><span class="boldnormal">G</span>onzo</p>
</div>
</body>
</html>

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

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Testing first-letter handling of font/text styles</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html { overflow: hidden; }
body {
margin: 20px;
font-size: 600%;
line-height: 1.2em;
font-family: serif;
}
div { width: 5em; }
p { width: 0.5em; margin: 0; font-style: italic; }
p.floatingfirst::first-letter { float: left; font-weight: bold; text-transform: uppercase; font-style: normal; }
</style>
</head>
<body>
<div>
<p class="floatingfirst">gonzo</p>
</div>
</body>
</html>

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

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Testing first-letter handling of font/text styles</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html { overflow: hidden; }
body {
margin: 1em;
font-size: 25px;
line-height: 1.2em;
font-family: serif;
}
div { width: 200px; }
p { width: 0.5em; margin: 0; }
span.bold { font-weight: bold; }
span.bolditalic { font-weight: bold; font-style: italic; }
span.floater { float: left; }
span.sans { font-family: sans-serif; }
</style>
</head>
<body>
<div>
<p><span class="bold">Y</span>ellow corndog</p>
<p><span class="bolditalic">h</span>IPPIE BANANA</p>
<p><span class="floater"><span class="sans">G</span>onzo macadamia</span></p>
<p><span class="bolditalic">f</span>ELICITOUS <span class="bold">R</span>aspberry</p>
</div>
</body>
</html>

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

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Testing first-letter handling of font/text styles</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html { overflow: hidden; }
body {
margin: 1em;
font-size: 25px;
line-height: 1.2em;
font-family: serif;
}
div { width: 200px; }
p { width: 0.5em; margin: 0; }
p:first-letter { font-weight: bold; text-transform: uppercase; }
p.reverse:first-letter { font-style: italic; text-transform: lowercase; }
span.floater { float: left; }
span.floater:first-letter { font-family: sans-serif; text-transform: uppercase; }
span.special { display: inline-block; }
span.special:first-letter { font-weight: bold; text-transform: uppercase; }
</style>
</head>
<body>
<div>
<p>yellow corndog</p>
<p class="reverse">HIPPIE BANANA</p>
<p><span class="floater">gonzo macadamia</span></p>
<p class="reverse">FELICITOUS <span class="special">raspberry</span></p>
</div>
</body>
</html>

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

@ -63,3 +63,5 @@ fails-if(winWidget||cocoaWidget) == 617869-1.html 617869-1-ref.html
== 723509-1.html 723509-1-ref.html
== 922550-1.html 922550-1-ref.html
== 958249.html 958249-ref.html
== font-text-styles.html font-text-styles-ref.html
fails-if(gtk2Widget) random-if(winWidget&&!d2d) == font-text-styles-floater.html font-text-styles-floater-ref.html # bug 992846

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

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Simple first-line test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html { overflow: hidden; }
body {
margin: 20px;
font-family: serif;
font-size: 200%;
line-height: 1.5;
width: 400px;
}
div:first-line {
font-family: sans-serif;
}
.floatright {
float: right;
}
</style>
</head>
<body><div>sans-serif<br>serif <strong>bold serif <em>italic</em></strong><em><br>just italic<span class="floatright">italic too</span></em></div></body>
</html>

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

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Simple first-line test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 20px;
font-family: serif;
font-size: 200%;
line-height: 1.5;
width: 400px;
}
span.sansserif {
font-family: sans-serif;
}
.floatright {
float: right;
}
</style>
</head>
<body><div><span class="sansserif">sans-serif</span><br>serif <strong>bold serif <em>italic</em></strong><em><br>just italic<span class="floatright">italic too</span></em></div></body>
</html>

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

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Simple first-line test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 20px;
font-family: serif;
font-size: 200%;
line-height: 1.5;
width: 400px;
}
div:first-line {
font-family: sans-serif;
}
.floatright {
float: right;
}
</style>
</head>
<body><div>sans-serif<br>serif <strong>bold serif <em>italic</em></strong><em><br>just italic<span class="floatright">italic too</span></em></div></body>
</html>

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

@ -33,3 +33,5 @@ load stress-10.html # crash test
== 469227-3.html 469227-3-ref.html
== restyle-inside-first-line.html restyle-inside-first-line-ref.html
== font-styles.html font-styles-ref.html
== font-styles-nooverflow.html font-styles-ref.html

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

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<style>
html { overflow: hidden; }
p:first-letter { text-transform: uppercase; }
p { text-transform: capitalize }
</style>
<body>
<p>blah blah</p>
</body>
</html>

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

@ -5,6 +5,7 @@
== capitalize-5.html capitalize-5-ref.html
== capitalize-6.html capitalize-6-ref.html
== capitalize-7.html capitalize-7-ref.html
== capitalize-7a.html capitalize-7-ref.html
== lowercase-1.html lowercase-ref.html
== lowercase-sigma-1.html lowercase-sigma-1-ref.html
== small-caps-1.html small-caps-1-ref.html

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

@ -3312,7 +3312,7 @@ SVGTextFrame::ScheduleReflowSVGNonDisplayText()
MOZ_ASSERT(f, "should have found an ancestor frame to reflow");
PresContext()->PresShell()->FrameNeedsReflow(
f, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
f, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
}
NS_IMPL_ISUPPORTS1(SVGTextFrame::MutationObserver, nsIMutationObserver)

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

@ -4,8 +4,9 @@
* 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/net/RemoteOpenFileChild.h"
#include "RemoteOpenFileChild.h"
#include "mozilla/unused.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/URIUtils.h"
@ -13,6 +14,7 @@
#include "nsThreadUtils.h"
#include "nsJARProtocolHandler.h"
#include "nsIRemoteOpenFileListener.h"
#include "nsProxyRelease.h"
// needed to alloc/free NSPR file descriptors
#include "private/pprio.h"
@ -83,8 +85,40 @@ RemoteOpenFileChild::RemoteOpenFileChild(const RemoteOpenFileChild& other)
RemoteOpenFileChild::~RemoteOpenFileChild()
{
if (mListener) {
NotifyListener(NS_ERROR_UNEXPECTED);
if (NS_IsMainThread()) {
if (mListener) {
NotifyListener(NS_ERROR_UNEXPECTED);
}
} else {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
if (mainThread) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mURI, true)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mAppURI, true)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mListener,
true)));
TabChild* tabChild;
mTabChild.forget(&tabChild);
if (tabChild) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(tabChild, &TabChild::Release);
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
}
} else {
using mozilla::unused;
NS_WARNING("RemoteOpenFileChild released after thread shutdown, leaking "
"its members!");
unused << mURI.forget();
unused << mAppURI.forget();
unused << mListener.forget();
unused << mTabChild.forget();
}
}
if (mNSPRFileDesc) {

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

@ -11,10 +11,10 @@ class TestSwitchWindow(MarionetteTestCase):
self.marionette.set_script_timeout(5000)
self.marionette.execute_async_script("""
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
.getService(Components.interfaces.nsIWindowWatcher);
var win = ww.openWindow(null, "chrome://browser/content/browser.xul", "testWin", null, null);
win.addEventListener("load", function() {
win.removeEventListener("load", arguments.callee, true);
win.addEventListener("load", function() {
win.removeEventListener("load", arguments.callee, true);
marionetteScriptFinished();
}, null);
""")
@ -24,7 +24,7 @@ win.addEventListener("load", function() {
self.marionette.set_context("chrome")
self.marionette.execute_script("""
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
.getService(Components.interfaces.nsIWindowWatcher);
var win = ww.getWindowByName("testWin", null);
if (win != null)
win.close();
@ -39,7 +39,7 @@ if (win != null)
self.assertEqual(self.marionette.current_window_handle, orig_win)
now_available = self.marionette.window_handles
#assert we can find the new window
self.assertEqual(len(now_available), len(orig_available) + 1)
self.assertEqual(len(now_available), len(orig_available) + 1)
#assert that our window is there
self.assertTrue(orig_win in now_available)
new_win = None
@ -59,16 +59,16 @@ if (win != null)
test_html = self.marionette.absolute_url("test_windows.html")
self.marionette.navigate(test_html)
current = self.marionette.current_window_handle
self.marionette.find_element('link text',"Open new window").click()
window_handles = self.marionette.window_handles
window_handles.remove(current)
self.marionette.switch_to_window(window_handles[0])
self.assertEqual(self.marionette.title, "We Arrive Here")
handle = self.marionette.current_window_handle
self.assertEqual(self.marionette.current_window_handle, handle)
self.assertEqual(2, len(self.marionette.window_handles))

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

@ -28,6 +28,9 @@ b2g = false
[test_getattr.py]
[test_getattr_chrome.py]
b2g = false
[test_elementsize.py]
[test_position.py]
[test_rendered_element.py]
[test_elementState.py]
[test_elementState_chrome.py]
b2g = false
@ -35,6 +38,10 @@ b2g = false
[test_text_chrome.py]
disabled = "Bug 896046"
[test_clearing.py]
[test_typing.py]
disabled = "Bug 123456"
[test_log.py]
[test_emulator.py]
browser = false
@ -77,12 +84,15 @@ browser = false
[test_specialpowers.py]
[test_switch_frame.py]
[test_switch_frame_chrome.py]
[test_switch_remote_frame.py]
[test_visibility.py]
[test_pagesource.py]
[test_visibility.py]
[test_window_switching.py]
disabled = "Bug 990298"
[test_window_management.py]
disabled = "Bug 990299"
b2g = false
[test_appcache.py]
@ -103,4 +113,6 @@ disabled = "Bug 925688"
browser = false
[test_errors.py]
[include:oop/manifest.ini]
[test_execute_isolate.py]
[include:oop/manifest.ini]

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

@ -18,8 +18,8 @@
}
#r2 {
background-color: red;
left: 10.9px;
top: 10.1px;
left: 11px;
top: 10px;
width: 48.666666667px;
height: 49.333333333px;
}

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

@ -770,12 +770,15 @@ AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI,
dataSurface->Unmap();
} else {
size.width = GetSystemMetrics(SM_CXSMICON);
size.height = GetSystemMetrics(SM_CYSMICON);
if (!size.width || !size.height) {
size.width = 16;
size.height = 16;
}
// By using the input image surface's size, we may end up encoding
// to a different size than a 16x16 (or bigger for higher DPI) ICO, but
// Windows will resize appropriately for us. If we want to encode ourselves
// one day because we like our resizing better, we'd have to manually
// resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
// SM_CYSMICON. We don't support resizing images asynchronously at the
// moment anyway so getting the DPI aware icon size won't help.
size.width = surface->GetSize().width;
size.height = surface->GetSize().height;
dataSurface = surface->GetDataSurface();
}
@ -823,11 +826,6 @@ NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run()
{
NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread.");
// Get the recommended icon width and height, or if failure to obtain
// these settings, fall back to 16x16 ICOs. These values can be different
// if the user has a different DPI setting other than 100%.
// Windows would scale the 16x16 icon themselves, but it's better
// we let our ICO encoder do it.
nsCOMPtr<nsIInputStream> iconStream;
nsRefPtr<imgIEncoder> encoder =
do_CreateInstance("@mozilla.org/image/encoder;2?"