Merging mozilla-inbound into mozilla-central.

This commit is contained in:
Mounir Lamouri 2011-08-11 13:08:47 +02:00
commit 43d40b8987
72 changed files with 1475 additions and 940 deletions

View File

@ -87,6 +87,9 @@ public class WatcherService extends Service
public static final int NOTIFICATION_ID = 1964;
boolean bInstalling = false;
@SuppressWarnings("unchecked")
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
@SuppressWarnings("unchecked")
private static final Class[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
@ -95,13 +98,16 @@ public class WatcherService extends Service
boolean.class};
private NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
private IWatcherService.Stub stub = new IWatcherService.Stub() {
@Override
public int UpdateApplication(String sAppName, String sFileName, String sOutFile, int bReboot) throws RemoteException
{
return UpdtApp(sAppName, sFileName, sOutFile, bReboot);
@ -279,6 +285,12 @@ public class WatcherService extends Service
// Running on an older platform.
mStartForeground = mStopForeground = null;
}
try {
mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature);
}
catch (NoSuchMethodException e) {
mSetForeground = null;
}
Notification notification = new Notification();
startForegroundCompat(R.string.foreground_service_started, notification);
}
@ -308,7 +320,18 @@ public class WatcherService extends Service
}
// Fall back on the old API.
setForeground(true);
if (mSetForeground != null) {
try {
mSetForegroundArgs[0] = Boolean.TRUE;
mSetForeground.invoke(this, mSetForegroundArgs);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
mNM.notify(id, notification);
}
@ -335,7 +358,18 @@ public class WatcherService extends Service
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
setForeground(false);
if (mSetForeground != null) {
try {
mSetForegroundArgs[0] = Boolean.FALSE;
mSetForeground.invoke(this, mSetForegroundArgs);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public void doToast(String sMsg)
@ -840,6 +874,7 @@ public class WatcherService extends Service
runner.start();
}
@Override
public void run() {
bInstalling = true;
UpdtApp(msPkgName, msPkgFileName, msOutFile, mbReboot);

View File

@ -303,11 +303,39 @@ case "$target" in
fi
if test -z "$android_toolchain" ; then
android_toolchain="$android_ndk"/build/prebuilt/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/arm-eabi-4.4.0
AC_MSG_CHECKING([for android toolchain directory])
kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"`
android_toolchain="$android_ndk"/build/prebuilt/$kernel_name-x86/arm-eabi-4.4.0
# With newer NDK, the toolchain path has changed.
if ! test -d "$android_toolchain" ; then
android_toolchain="$android_ndk"/toolchains/arm-$kernel_name-androideabi-4.4.3/prebuilt/$kernel_name-x86
fi
if test -d "$android_toolchain" ; then
AC_MSG_RESULT([$android_toolchain])
else
AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.])
fi
fi
if test -z "$android_platform" ; then
android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
AC_MSG_CHECKING([for android platform directory])
android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
# With newer NDK, the platform path has changed.
if ! test -d "$android_platform" ; then
android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_cpu"
fi
if test -d "$android_platform" ; then
AC_MSG_RESULT([$android_platform])
else
AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.])
fi
fi
dnl set up compilers

View File

@ -860,6 +860,12 @@ nsAttrAndChildArray::SizeOf() const
// so, we just have to compute the size of *mBuffer given that this object
// doesn't own the children list.
size += mImpl->mBufferSize * sizeof(*(mImpl->mBuffer)) + NS_IMPL_EXTRA_SIZE;
PRUint32 slotCount = AttrSlotCount();
for (PRUint32 i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
nsAttrValue* value = &ATTRS(mImpl)[i].mValue;
size += value->SizeOf() - sizeof(*value);
}
}
return size;

View File

@ -1448,3 +1448,63 @@ nsAttrValue::StringToInteger(const nsAString& aValue, PRBool* aStrict,
nsAutoString tmp(aValue);
return tmp.ToInteger(aErrorCode);
}
PRInt64
nsAttrValue::SizeOf() const
{
PRInt64 size = sizeof(*this);
switch (BaseType()) {
case eStringBase:
{
// TODO: we might be counting the string size more than once.
// This should be fixed with bug 677487.
nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
size += str ? str->StorageSize() : 0;
break;
}
case eOtherBase:
{
MiscContainer* container = GetMiscContainer();
if (!container) {
break;
}
size += sizeof(*container);
void* otherPtr = MISC_STR_PTR(container);
// We only count the size of the object pointed by otherPtr if it's a
// string. When it's an atom, it's counted separatly.
if (otherPtr &&
static_cast<ValueBaseType>(container->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) {
// TODO: we might be counting the string size more than once.
// This should be fixed with bug 677487.
nsStringBuffer* str = static_cast<nsStringBuffer*>(otherPtr);
size += str ? str->StorageSize() : 0;
}
// TODO: mCSSStyleRule and mSVGValue might be owned by another object
// which would make us count them twice, bug 677493.
if (Type() == eCSSStyleRule && container->mCSSStyleRule) {
// TODO: Add SizeOf() to StyleRule, bug 677503.
size += sizeof(*container->mCSSStyleRule);
} else if (Type() == eSVGValue && container->mSVGValue) {
// TODO: Add SizeOf() to nsSVGValue, bug 677504.
size += sizeof(*container->mSVGValue);
} else if (Type() == eAtomArray && container->mAtomArray) {
size += sizeof(container->mAtomArray) + sizeof(nsTArrayHeader);
size += container->mAtomArray->Capacity() * sizeof(nsCOMPtr<nsIAtom>);
// Don't count the size of each nsIAtom, they are counted separatly.
}
break;
}
case eAtomBase: // Atoms are counted separatly.
case eIntegerBase: // The value is in mBits, nothing to do.
break;
}
return size;
}

View File

@ -310,6 +310,8 @@ public:
*/
PRBool ParseIntMarginValue(const nsAString& aString);
PRInt64 SizeOf() const;
private:
// These have to be the same as in ValueType
enum ValueBaseType {

View File

@ -277,3 +277,19 @@ nsMappedAttributes::IndexOfAttr(nsIAtom* aLocalName, PRInt32 aNamespaceID) const
return -1;
}
PRInt64
nsMappedAttributes::SizeOf() const
{
NS_ASSERTION(mAttrCount == mBufferSize,
"mBufferSize and mAttrCount are expected to be the same.");
PRInt64 size = sizeof(*this) - sizeof(void*) + mAttrCount * sizeof(InternalAttr);
for (PRUint16 i = 0; i < mAttrCount; ++i) {
size += Attrs()[i].mValue.SizeOf() - sizeof(Attrs()[i].mValue);
}
return size;
}

View File

@ -108,11 +108,7 @@ public:
virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const;
#endif
PRInt64 SizeOf() const {
NS_ASSERTION(mAttrCount == mBufferSize,
"mBufferSize and mAttrCount are expected to be the same.");
return sizeof(*this) - sizeof(void*) + mAttrCount * sizeof(InternalAttr);
}
PRInt64 SizeOf() const;
private:
nsMappedAttributes(const nsMappedAttributes& aCopy);

View File

@ -649,9 +649,9 @@ ImageDocument::CreateSyntheticDocument()
// This is bad during printing, it means tall image frames won't know
// the size of the paper and cannot break into continuations along
// multiple pages.
Element* body = GetBodyElement();
if (!body) {
NS_WARNING("no body on image document!");
Element* head = GetHeadElement();
if (!head) {
NS_WARNING("no head on image document!");
return NS_ERROR_FAILURE;
}
@ -666,9 +666,15 @@ ImageDocument::CreateSyntheticDocument()
}
styleContent->SetTextContent(NS_LITERAL_STRING("img { display: block; }"));
body->AppendChildTo(styleContent, PR_FALSE);
head->AppendChildTo(styleContent, PR_FALSE);
// Add the image element
Element* body = GetBodyElement();
if (!body) {
NS_WARNING("no body on image document!");
return NS_ERROR_FAILURE;
}
nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nsnull,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);

View File

@ -103,6 +103,8 @@ _TEST_FILES = test_bug1682.html \
bug499092.html \
test_bug512367.html \
test_bug571981.html \
test_bug677495.html \
test_bug677495-1.html \
$(NULL)
ifneq (mobile,$(MOZ_BUILD_APP))

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=677495
As mandated by the spec, the body of a media document must only contain one child.
-->
<head>
<title>Test for Bug 571981</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
function frameLoaded() {
var testframe = document.getElementById('testframe');
var testframeChildren = testframe.contentDocument.body.childNodes;
is(testframeChildren.length, 1, "Body of video document has 1 child");
is(testframeChildren[0].nodeName, "VIDEO", "Only child of body must be a <video> element");
SimpleTest.finish();
}
</script>
</head>
<body>
<p id="display"></p>
<iframe id="testframe" name="testframe" onload="frameLoaded()"
src="data:video/webm,"></iframe>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=677495
As mandated by the spec, the body of a media document must only contain one child.
-->
<head>
<title>Test for Bug 571981</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
function frameLoaded() {
var testframe = document.getElementById('testframe');
var testframeChildren = testframe.contentDocument.body.childNodes;
is(testframeChildren.length, 1, "Body of image document has 1 child");
is(testframeChildren[0].nodeName, "IMG", "Only child of body must be an <img> element");
SimpleTest.finish();
}
</script>
</head>
<body>
<p id="display"></p>
<iframe id="testframe" name="testframe" onload="frameLoaded()"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oMFgQGMyFwHucAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC"></iframe>
</body>
</html>

View File

@ -3518,7 +3518,13 @@ PluginInstanceChild::SwapSurfaces()
mDoubleBufferCARenderer.ClearFrontSurface();
}
#endif //MOZ_WIDGET_COCOA
#else
if (mCurrentSurface && mBackSurface &&
(mCurrentSurface->GetSize() != mBackSurface->GetSize() ||
mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) {
ClearCurrentSurface();
}
#endif
}
void

View File

@ -4724,6 +4724,10 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection,
{
// here's where we actually figure out what to do
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
// Ignore all non-editable nodes. Leave them be.
if (!mHTMLEditor->IsEditable(curNode)) continue;
PRInt32 offset;
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
NS_ENSURE_SUCCESS(res, res);
@ -8704,7 +8708,23 @@ nsHTMLEditRules::RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignTyp
res = mHTMLEditor->NodeIsBlockStatic(child, &isBlock);
NS_ENSURE_SUCCESS(res, res);
if ((isBlock && !nsHTMLEditUtils::IsDiv(child)) || nsHTMLEditUtils::IsHR(child))
if (nsEditor::NodeIsType(child, nsEditProperty::center))
{
// the current node is a CENTER element
// first remove children's alignment
res = RemoveAlignment(child, aAlignType, PR_TRUE);
NS_ENSURE_SUCCESS(res, res);
// we may have to insert BRs in first and last position of element's children
// if the nodes before/after are not blocks and not BRs
res = MakeSureElemStartsOrEndsOnCR(child);
NS_ENSURE_SUCCESS(res, res);
// now remove the CENTER container
res = mHTMLEditor->RemoveContainer(child);
NS_ENSURE_SUCCESS(res, res);
}
else if (isBlock || nsHTMLEditUtils::IsHR(child))
{
// the current node is a block element
nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(child);
@ -8734,47 +8754,6 @@ nsHTMLEditRules::RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignTyp
NS_ENSURE_SUCCESS(res, res);
}
}
else if (nsEditor::NodeIsType(child, nsEditProperty::center)
|| nsHTMLEditUtils::IsDiv(child))
{
// this is a CENTER or a DIV element and we have to remove it
// first remove children's alignment
res = RemoveAlignment(child, aAlignType, PR_TRUE);
NS_ENSURE_SUCCESS(res, res);
if (useCSS && nsHTMLEditUtils::IsDiv(child))
{
// if we are in CSS mode and if the element is a DIV, let's remove it
// if it does not carry any style hint (style attr, class or ID)
nsAutoString dummyCssValue;
res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue);
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMElement> childElt = do_QueryInterface(child);
PRBool hasStyleOrIdOrClass;
res = mHTMLEditor->HasStyleOrIdOrClass(childElt, &hasStyleOrIdOrClass);
NS_ENSURE_SUCCESS(res, res);
if (!hasStyleOrIdOrClass)
{
// we may have to insert BRs in first and last position of DIV's children
// if the nodes before/after are not blocks and not BRs
res = MakeSureElemStartsOrEndsOnCR(child);
NS_ENSURE_SUCCESS(res, res);
res = mHTMLEditor->RemoveContainer(child);
NS_ENSURE_SUCCESS(res, res);
}
}
else
{
// we may have to insert BRs in first and last position of element's children
// if the nodes before/after are not blocks and not BRs
res = MakeSureElemStartsOrEndsOnCR(child);
NS_ENSURE_SUCCESS(res, res);
// in HTML mode, let's remove the element
res = mHTMLEditor->RemoveContainer(child);
NS_ENSURE_SUCCESS(res, res);
}
}
child = tmp;
}
return NS_OK;

View File

@ -57,6 +57,7 @@ _TEST_FILES = \
test_bug417418.html \
test_bug432225.html \
test_bug439808.html \
test_bug442186.html \
test_bug449243.html \
test_bug455992.html \
test_bug456244.html \

View File

@ -0,0 +1,102 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=442186
-->
<head>
<title>Test for Bug 442186</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.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=442186">Mozilla Bug 442186</a>
<p id="display"></p>
<div id="content">
<h2> two &lt;div&gt; containers </h2>
<section contenteditable id="test1">
<div> First paragraph with some text. </div>
<div> Second paragraph with some text. </div>
</section>
<h2> two paragraphs </h2>
<section contenteditable id="test2">
<p> First paragraph with some text. </p>
<p> Second paragraph with some text. </p>
</section>
<h2> one text node, one paragraph </h2>
<section contenteditable id="test3">
First paragraph with some text.
<p> Second paragraph with some text. </p>
</section>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 442186 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests);
function justify(textNode, pos) {
if (!pos) pos = 10;
// put the caret on the requested character
var range = document.createRange();
var sel = window.getSelection();
range.setStart(textNode, pos);
range.setEnd(textNode, pos);
sel.addRange(range);
// align
document.execCommand("justifyright", false, null);
}
function runTests() {
const test1 = document.getElementById("test1");
const test2 = document.getElementById("test2");
const test3 = document.getElementById("test3");
// #test1: two <div> containers
const line1 = test1.querySelector("div").firstChild;
test1.focus();
justify(line1);
is(test1.querySelectorAll("*").length, 2,
"Aligning the first child should not create nor remove any element.");
is(line1.parentNode.nodeName.toLowerCase(), "div",
"Aligning the first <div> should not modify its node type.");
is(line1.parentNode.style.textAlign, "right",
"Aligning the first <div> should set a 'text-align: right' style rule.");
// #test2: two paragraphs
const line2 = test2.querySelector("p").firstChild;
test2.focus();
justify(line2);
is(test2.querySelectorAll("*").length, 2,
"Aligning the first child should not create nor remove any element.");
is(line2.parentNode.nodeName.toLowerCase(), "p",
"Aligning the first paragraph should not modify its node type.");
is(line2.parentNode.style.textAlign, "right",
"Aligning the first paragraph should set a 'text-align: right' style rule.");
// #test3: one text node, two paragraphs
const line3 = test3.firstChild;
test3.focus();
justify(line3);
is(test3.querySelectorAll("*").length, 2,
"Aligning the first child should create a block element.");
is(line3.parentNode.nodeName.toLowerCase(), "div",
"Aligning the first child should create a block element.");
is(line3.parentNode.style.textAlign, "right",
"Aligning the first line should set a 'text-align: right' style rule.");
// done
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -2558,6 +2558,15 @@ _cairo_d2d_acquire_source_image(void *abstract_surface,
size.width,
size.height,
data.RowPitch);
if (cairo_surface_status(&((*image_out)->base))) {
volatile cairo_status_t flambo[10];
for (int i=0; i<10; i++) {
flambo[i] = cairo_surface_status(&((*image_out)->base));
}
volatile int p = 0;
p = 5/p;
}
*image_extra = softTexture.forget();
return CAIRO_STATUS_SUCCESS;

View File

@ -1605,7 +1605,7 @@ namespace JSC {
void fnegd_r(int dd, int dm, Condition cc = AL)
{
js::JaegerSpew(js::JSpew_Insns,
IPFX "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
IPFX "%-15s %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
m_buffer.putInt(static_cast<ARMWord>(cc) | FNEGD | DD(dd) | DM(dm));
}

View File

@ -294,11 +294,39 @@ case "$target" in
fi
if test -z "$android_toolchain" ; then
android_toolchain="$android_ndk"/build/prebuilt/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/arm-eabi-4.4.0
AC_MSG_CHECKING([for android toolchain directory])
kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"`
android_toolchain="$android_ndk"/build/prebuilt/$kernel_name-x86/arm-eabi-4.4.0
# With newer NDK, the toolchain path has changed.
if ! test -d "$android_toolchain" ; then
android_toolchain="$android_ndk"/toolchains/arm-$kernel_name-androideabi-4.4.3/prebuilt/$kernel_name-x86
fi
if test -d "$android_toolchain" ; then
AC_MSG_RESULT([$android_toolchain])
else
AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.])
fi
fi
if test -z "$android_platform" ; then
android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
AC_MSG_CHECKING([for android platform directory])
android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
# With newer NDK, the platform path has changed.
if ! test -d "$android_platform" ; then
android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_cpu"
fi
if test -d "$android_platform" ; then
AC_MSG_RESULT([$android_platform])
else
AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.])
fi
fi
dnl set up compilers

View File

@ -4548,6 +4548,11 @@ GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
*result = FFI_STDCALL;
return true;
#elif (defined(_WIN64))
// We'd like the same code to work across Win32 and Win64, so stdcall_api
// and winapi_abi become aliases to the lone Win64 ABI.
*result = FFI_WIN64;
return true;
#endif
case INVALID_ABI:
break;
@ -4692,6 +4697,7 @@ FunctionType::BuildSymbolName(JSContext* cx,
break;
case ABI_STDCALL: {
#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
// On WIN32, stdcall functions look like:
// _foo@40
// where 'foo' is the function name, and '40' is the aligned size of the
@ -4708,6 +4714,11 @@ FunctionType::BuildSymbolName(JSContext* cx,
}
IntegerToString(size, 10, result);
#elif defined(_WIN64)
// On Win64, stdcall is an alias to the default ABI for compatibility, so no
// mangling is done.
AppendString(result, name);
#endif
break;
}

View File

@ -15,7 +15,7 @@ revertVersion();
for (vno in {160: null, 170: null, 180: null}) {
print('Setting version to: ' + vno);
version(vno);
version(Number(vno));
assertEq(syntaxErrorFromXML(), false);
revertVersion();
}

View File

@ -0,0 +1,11 @@
function f(x, y) {
for (var i = 0; i < 100; i++)
assertEq(x[0], y);
}
var a = ArrayBuffer(20);
var b = Int32Array(a, 12, 2);
var c = Int32Array(a, 0, 2);
b[0] = 10;
f(b, 10);
c[0] = 20;
f(c, 20);

View File

@ -176,7 +176,7 @@ ArenaHeader::checkSynchronizedWithFreeList() const
if (!compartment->rt->gcRunning)
return;
FreeSpan firstSpan(address() + firstFreeSpanStart, address() + firstFreeSpanEnd);
FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
if (firstSpan.isEmpty())
return;
FreeSpan *list = &compartment->freeLists.lists[getThingKind()];
@ -187,8 +187,7 @@ ArenaHeader::checkSynchronizedWithFreeList() const
* Here this arena has free things, FreeList::lists[thingKind] is not
* empty and also points to this arena. Thus they must the same.
*/
JS_ASSERT(firstSpan.start == list->start);
JS_ASSERT(firstSpan.end == list->end);
JS_ASSERT(firstSpan.isSameNonEmptySpan(list));
}
#endif
@ -200,7 +199,7 @@ Arena::finalize(JSContext *cx)
JS_ASSERT(!aheader.getMarkingDelay()->link);
uintptr_t thing = thingsStart(sizeof(T));
uintptr_t end = thingsEnd();
uintptr_t lastByte = thingsEnd() - 1;
FreeSpan nextFree(aheader.getFirstFreeSpan());
nextFree.checkSpan();
@ -213,15 +212,15 @@ Arena::finalize(JSContext *cx)
size_t nmarked = 0;
#endif
for (;; thing += sizeof(T)) {
JS_ASSERT(thing <= end);
if (thing == nextFree.start) {
JS_ASSERT(nextFree.end <= end);
if (nextFree.end == end)
JS_ASSERT(thing <= lastByte + 1);
if (thing == nextFree.first) {
JS_ASSERT(nextFree.last <= lastByte);
if (nextFree.last == lastByte)
break;
JS_ASSERT(Arena::isAligned(nextFree.end, sizeof(T)));
JS_ASSERT(Arena::isAligned(nextFree.last, sizeof(T)));
if (!newFreeSpanStart)
newFreeSpanStart = thing;
thing = nextFree.end;
thing = nextFree.last;
nextFree = *nextFree.nextSpan();
nextFree.checkSpan();
} else {
@ -233,10 +232,10 @@ Arena::finalize(JSContext *cx)
#endif
if (newFreeSpanStart) {
JS_ASSERT(thing >= thingsStart(sizeof(T)) + sizeof(T));
newListTail->start = newFreeSpanStart;
newListTail->end = thing - sizeof(T);
newListTail = newListTail->nextSpanUnchecked();
newFreeSpanStart = 0;
newListTail->first = newFreeSpanStart;
newListTail->last = thing - sizeof(T);
newListTail = newListTail->nextSpanUnchecked(sizeof(T));
newFreeSpanStart = NULL;
}
} else {
if (!newFreeSpanStart)
@ -253,20 +252,20 @@ Arena::finalize(JSContext *cx)
return true;
}
newListTail->start = newFreeSpanStart ? newFreeSpanStart : nextFree.start;
JS_ASSERT(Arena::isAligned(newListTail->start, sizeof(T)));
newListTail->end = end;
newListTail->first = newFreeSpanStart ? newFreeSpanStart : nextFree.first;
JS_ASSERT(Arena::isAligned(newListTail->first, sizeof(T)));
newListTail->last = lastByte;
#ifdef DEBUG
size_t nfree = 0;
for (FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) {
for (const FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) {
span->checkSpan();
JS_ASSERT(Arena::isAligned(span->start, sizeof(T)));
JS_ASSERT(Arena::isAligned(span->end, sizeof(T)));
nfree += (span->end - span->start) / sizeof(T) + 1;
JS_ASSERT(Arena::isAligned(span->first, sizeof(T)));
JS_ASSERT(Arena::isAligned(span->last, sizeof(T)));
nfree += (span->last - span->first) / sizeof(T) + 1;
JS_ASSERT(nfree + nmarked <= thingsPerArena(sizeof(T)));
}
nfree += (newListTail->end - newListTail->start) / sizeof(T);
nfree += (newListTail->last + 1 - newListTail->first) / sizeof(T);
JS_ASSERT(nfree + nmarked == thingsPerArena(sizeof(T)));
#endif
aheader.setFirstFreeSpan(&newListHead);
@ -629,9 +628,9 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr)
FreeSpan firstSpan(aheader->getFirstFreeSpan());
for (FreeSpan *span = &firstSpan;;) {
for (const FreeSpan *span = &firstSpan;;) {
/* If the thing comes fore the current span, it's not free. */
if (addr < span->start)
if (addr < span->first)
return false;
/*
@ -639,7 +638,7 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr)
* "<" even for the last span as we know that thing is inside the
* arena. Thus for the last span thing < span->end.
*/
if (addr <= span->end)
if (addr <= span->last)
return true;
/*
@ -1364,7 +1363,7 @@ IsGCAllowed(JSContext *cx)
}
template <typename T>
inline Cell *
inline void *
RefillTypedFreeList(JSContext *cx, unsigned thingKind)
{
JS_ASSERT(!cx->runtime->gcRunning);
@ -1391,7 +1390,7 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind)
* things and populate the free list. If that happens, just
* return that list head.
*/
if (Cell *thing = compartment->freeLists.getNext(thingKind, sizeof(T)))
if (void *thing = compartment->freeLists.getNext(thingKind, sizeof(T)))
return thing;
}
ArenaHeader *aheader =
@ -1414,7 +1413,7 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind)
return NULL;
}
Cell *
void *
RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
{
switch (thingKind) {
@ -1452,7 +1451,7 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
#endif
default:
JS_NOT_REACHED("bad finalize kind");
return NULL;
return 0;
}
}
@ -2320,7 +2319,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
WatchpointMap::sweepAll(rt);
/*
* We finalize objects before other GC things to ensure that object's finalizer
* We finalize objects before other GC things to ensure that object's finalizer
* can access them even if they will be freed. Sweep the runtime's property trees
* after finalizing objects, in case any had watchpoints referencing tree nodes.
* Do this before sweeping compartments, so that we sweep all shapes in
@ -2832,18 +2831,18 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data,
Arena *arena = aheader->getArena();
(*arenaCallback)(cx, data, arena, traceKind, thingSize);
FreeSpan firstSpan(aheader->getFirstFreeSpan());
FreeSpan *span = &firstSpan;
const FreeSpan *span = &firstSpan;
for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
JS_ASSERT(thing <= arena->thingsEnd());
if (thing == span->start) {
if (thing == span->first) {
if (!span->hasNext())
break;
thing = span->end;
thing = span->last;
span = span->nextSpan();
} else {
(*cellCallback)(cx, data, reinterpret_cast<void *>(thing), traceKind,
thingSize);
void *t = reinterpret_cast<void *>(thing);
(*cellCallback)(cx, data, t, traceKind, thingSize);
}
}
}

View File

@ -126,100 +126,221 @@ const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD;
/*
* A FreeSpan represents a contiguous sequence of free cells in an Arena.
* |start| is the address of the first free cell in the span. |end| is the
* address of the last free cell in the span. The last cell (starting at
* |end|) holds a FreeSpan data structure for the next span. However, the last
* FreeSpan in an Arena is special: |end| points to the end of the Arena (an
* unusable address), and no next FreeSpan is stored there.
* |first| is the address of the first free cell in the span. |last| is the
* address of the last free cell in the span. This last cell holds a FreeSpan
* data structure for the next span unless this is the last span on the list
* of spans in the arena. For this last span |last| points to the last byte of
* the last thing in the arena and no linkage is stored there, so
* |last| == arenaStart + ArenaSize - 1. If the space at the arena end is
* fully used this last span is empty and |first| == |last + 1|.
*
* As things in the arena ends on its boundary that is aligned on ArenaSize,
* end & ArenaMask is zero if and only if the span is last. Also, since the
* first thing in the arena comes after the header, start & ArenaSize is zero
* if and only if the span is the empty span at the end of the arena.
* Thus |first| < |last| implies that we have either the last span with at least
* one element or that the span is not the last and contains at least 2
* elements. In both cases to allocate a thing from this span we need simply
* to increment |first| by the allocation size.
*
* The type of the start and end fields is uintptr_t, not a pointer type, to
* minimize the amount of casting when doing mask operations.
* |first| == |last| implies that we have a one element span that records the
* next span. So to allocate from it we need to update the span list head
* with a copy of the span stored at |last| address so the following
* allocations will use that span.
*
* |first| > |last| implies that we have an empty last span and the arena is
* fully used.
*
* Also only for the last span (|last| & 1)! = 0 as all allocation sizes are
* multiples of Cell::CellSize.
*/
struct FreeSpan {
uintptr_t start;
uintptr_t end;
uintptr_t first;
uintptr_t last;
public:
FreeSpan() { }
FreeSpan() {}
FreeSpan(uintptr_t start, uintptr_t end)
: start(start), end(end) {
FreeSpan(uintptr_t first, uintptr_t last)
: first(first), last(last) {
checkSpan();
}
/*
* To minimize the size of the arena header the first span is encoded
* there as offsets from the arena start.
*/
static size_t encodeOffsets(size_t firstOffset, size_t lastOffset = ArenaSize - 1) {
/* Check that we can pack the offsets into uint16. */
JS_STATIC_ASSERT(ArenaShift < 16);
JS_ASSERT(firstOffset <= ArenaSize);
JS_ASSERT(lastOffset < ArenaSize);
JS_ASSERT(firstOffset <= ((lastOffset + 1) & ~size_t(1)));
return firstOffset | (lastOffset << 16);
}
static const size_t EmptyOffsets = ArenaSize | ((ArenaSize - 1) << 16);
static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) {
JS_ASSERT(!(arenaAddr & ArenaMask));
size_t firstOffset = offsets & 0xFFFF;
size_t lastOffset = offsets >> 16;
JS_ASSERT(firstOffset <= ArenaSize);
JS_ASSERT(lastOffset < ArenaSize);
/*
* We must not use | when calculating first as firstOffset is
* ArenaMask + 1 for the empty span.
*/
return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset);
}
void initAsEmpty(uintptr_t arenaAddr = 0) {
JS_ASSERT(!(arenaAddr & ArenaMask));
first = arenaAddr + ArenaSize;
last = arenaAddr | (ArenaSize - 1);
JS_ASSERT(isEmpty());
}
bool isEmpty() const {
checkSpan();
return !(start & ArenaMask);
return first > last;
}
bool hasNext() const {
checkSpan();
return !!(end & ArenaMask);
return !(last & uintptr_t(1));
}
FreeSpan *nextSpan() const {
const FreeSpan *nextSpan() const {
JS_ASSERT(hasNext());
return reinterpret_cast<FreeSpan *>(end);
return reinterpret_cast<FreeSpan *>(last);
}
FreeSpan *nextSpanUnchecked() const {
JS_ASSERT(end & ArenaMask);
return reinterpret_cast<FreeSpan *>(end);
FreeSpan *nextSpanUnchecked(size_t thingSize) const {
#ifdef DEBUG
uintptr_t lastOffset = last & ArenaMask;
JS_ASSERT(!(lastOffset & 1));
JS_ASSERT((ArenaSize - lastOffset) % thingSize == 0);
#endif
return reinterpret_cast<FreeSpan *>(last);
}
uintptr_t arenaAddressUnchecked() const {
return last & ~ArenaMask;
}
uintptr_t arenaAddress() const {
checkSpan();
return arenaAddressUnchecked();
}
ArenaHeader *arenaHeader() const {
return reinterpret_cast<ArenaHeader *>(arenaAddress());
}
bool isSameNonEmptySpan(const FreeSpan *another) const {
JS_ASSERT(!isEmpty());
return start & ~ArenaMask;
JS_ASSERT(!another->isEmpty());
return first == another->first && last == another->last;
}
bool isWithinArena(uintptr_t arenaAddr) const {
JS_ASSERT(!(arenaAddr & ArenaMask));
/* Return true for the last empty span as well. */
return arenaAddress() == arenaAddr;
}
size_t encodeAsOffsets() const {
/*
* We must use first - arenaAddress(), not first & ArenaMask as
* first == ArenaMask + 1 for an empty span.
*/
uintptr_t arenaAddr = arenaAddress();
return encodeOffsets(first - arenaAddr, last & ArenaMask);
}
/* See comments before FreeSpan for details. */
JS_ALWAYS_INLINE void *allocate(size_t thingSize) {
JS_ASSERT(thingSize % Cell::CellSize == 0);
checkSpan();
uintptr_t thing = first;
if (thing < last) {
/* Bump-allocate from the current span. */
first = thing + thingSize;
} else if (JS_LIKELY(thing == last)) {
/*
* Move to the next span. We use JS_LIKELY as without PGO
* compilers mis-predict == here as unlikely to succeed.
*/
*this = *reinterpret_cast<FreeSpan *>(thing);
} else {
return NULL;
}
checkSpan();
return reinterpret_cast<void *>(thing);
}
void checkSpan() const {
#ifdef DEBUG
JS_ASSERT(start <= end);
JS_ASSERT(end - start <= ArenaSize);
if (!(start & ArenaMask)) {
/* The span is last and empty. */
JS_ASSERT(start == end);
/* We do not allow spans at the end of the address space. */
JS_ASSERT(last != uintptr_t(-1));
JS_ASSERT(first);
JS_ASSERT(last);
JS_ASSERT(first - 1 <= last);
uintptr_t arenaAddr = arenaAddressUnchecked();
if (last & 1) {
/* The span is the last. */
JS_ASSERT((last & ArenaMask) == ArenaMask);
if (first - 1 == last) {
/* The span is last and empty. The above start != 0 check
* implies that we are not at the end of the address space.
*/
return;
}
size_t spanLength = last - first + 1;
JS_ASSERT(spanLength % Cell::CellSize == 0);
/* Start and end must belong to the same arena. */
JS_ASSERT((first & ~ArenaMask) == arenaAddr);
return;
}
JS_ASSERT(start);
JS_ASSERT(end);
uintptr_t arena = start & ~ArenaMask;
if (!(end & ArenaMask)) {
/* The last span with few free things at the end of the arena. */
JS_ASSERT(arena + ArenaSize == end);
return;
}
/* The span is not the last and we have more spans to follow. */
JS_ASSERT(first <= last);
size_t spanLengthWithoutOneThing = last - first;
JS_ASSERT(spanLengthWithoutOneThing % Cell::CellSize == 0);
/* The span is not last and we have at least one span that follows it.*/
JS_ASSERT(arena == (end & ~ArenaMask));
FreeSpan *next = reinterpret_cast<FreeSpan *>(end);
JS_ASSERT((first & ~ArenaMask) == arenaAddr);
/*
* If there is not enough space before the arena end to allocate one
* more thing, then the span must be marked as the last one to avoid
* storing useless empty span reference.
*/
size_t beforeTail = ArenaSize - (last & ArenaMask);
JS_ASSERT(beforeTail >= sizeof(FreeSpan) + Cell::CellSize);
FreeSpan *next = reinterpret_cast<FreeSpan *>(last);
/*
* The GC things on the list of free spans come from one arena
* and the spans are linked in ascending address order with
* at least one non-free thing between spans.
*/
JS_ASSERT(end < next->start);
JS_ASSERT(last < next->first);
JS_ASSERT(arenaAddr == next->arenaAddressUnchecked());
if (!(next->start & ArenaMask)) {
if (next->first > next->last) {
/*
* The next span is the empty span that terminates the list for
* arenas that do not have any free things at the end.
*/
JS_ASSERT(next->start == next->end);
JS_ASSERT(arena + ArenaSize == next->start);
} else {
/* The next spans is not empty and must starts inside the arena. */
JS_ASSERT(arena == (next->start & ~ArenaMask));
JS_ASSERT(next->first - 1 == next->last);
JS_ASSERT(arenaAddr + ArenaSize == next->first);
}
#endif
}
};
/* Every arena has a header. */
@ -231,12 +352,9 @@ struct ArenaHeader {
/*
* The first span of free things in the arena. We encode it as the start
* and end offsets within the arena, not as FreeSpan structure, to
* minimize the header size. When the arena has no free things, the span
* must be the empty one pointing to the arena's end. For such a span the
* start and end offsets must be ArenaSize.
* minimize the header size.
*/
uint16_t firstFreeSpanStart;
uint16_t firstFreeSpanEnd;
size_t firstFreeSpanOffsets;
/*
* One of FinalizeKind constants or FINALIZE_LIMIT when the arena does not
@ -263,8 +381,12 @@ struct ArenaHeader {
inline void init(JSCompartment *comp, unsigned thingKind, size_t thingSize);
uintptr_t arenaAddress() const {
return address();
}
Arena *getArena() {
return reinterpret_cast<Arena *>(address());
return reinterpret_cast<Arena *>(arenaAddress());
}
unsigned getThingKind() const {
@ -273,26 +395,23 @@ struct ArenaHeader {
}
bool hasFreeThings() const {
return firstFreeSpanStart != ArenaSize;
return firstFreeSpanOffsets != FreeSpan::EmptyOffsets;
}
void setAsFullyUsed() {
firstFreeSpanStart = firstFreeSpanEnd = uint16_t(ArenaSize);
firstFreeSpanOffsets = FreeSpan::EmptyOffsets;
}
FreeSpan getFirstFreeSpan() const {
#ifdef DEBUG
checkSynchronizedWithFreeList();
#endif
return FreeSpan(address() + firstFreeSpanStart, address() + firstFreeSpanEnd);
return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
}
void setFirstFreeSpan(const FreeSpan *span) {
span->checkSpan();
JS_ASSERT(span->start - address() <= ArenaSize);
JS_ASSERT(span->end - address() <= ArenaSize);
firstFreeSpanStart = uint16_t(span->start - address());
firstFreeSpanEnd = uint16_t(span->end - address());
JS_ASSERT(span->isWithinArena(arenaAddress()));
firstFreeSpanOffsets = span->encodeAsOffsets();
}
inline MarkingDelay *getMarkingDelay() const;
@ -549,8 +668,7 @@ ArenaHeader::init(JSCompartment *comp, unsigned kind, size_t thingSize)
JS_ASSERT(!getMarkingDelay()->link);
compartment = comp;
thingKind = kind;
firstFreeSpanStart = uint16_t(Arena::thingsStartOffset(thingSize));
firstFreeSpanEnd = uint16_t(ArenaSize);
firstFreeSpanOffsets = FreeSpan::encodeOffsets(Arena::thingsStartOffset(thingSize));
}
inline uintptr_t
@ -806,7 +924,7 @@ struct FreeLists {
void init() {
for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i)
lists[i].start = lists[i].end = 0;
lists[i].initAsEmpty();
}
/*
@ -817,10 +935,10 @@ struct FreeLists {
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
FreeSpan *list = &lists[i];
if (!list->isEmpty()) {
ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(list);
list->start = list->end = 0;
list->initAsEmpty();
}
}
}
@ -834,7 +952,7 @@ struct FreeLists {
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
FreeSpan *list = &lists[i];
if (!list->isEmpty()) {
ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(list);
}
@ -849,47 +967,22 @@ struct FreeLists {
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
FreeSpan *list = &lists[i];
if (!list->isEmpty()) {
ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
#ifdef DEBUG
FreeSpan span(aheader->getFirstFreeSpan());
JS_ASSERT(span.start == list->start);
JS_ASSERT(span.end == list->end);
#endif
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
aheader->setAsFullyUsed();
}
}
}
JS_ALWAYS_INLINE Cell *getNext(unsigned thingKind, size_t thingSize) {
FreeSpan *list = &lists[thingKind];
list->checkSpan();
uintptr_t thing = list->start;
if (thing != list->end) {
/*
* We either have at least one thing in the span that ends the
* arena list or we have at least two things in the non-last span.
* In both cases we just need to bump the start pointer to account
* for the allocation.
*/
list->start += thingSize;
JS_ASSERT(list->start <= list->end);
} else if (thing & ArenaMask) {
/*
* The thing points to the last thing in the span that has at
* least one more span to follow. Return the thing and update
* the list with that next span.
*/
*list = *list->nextSpan();
} else {
return NULL;
}
return reinterpret_cast<Cell *>(thing);
JS_ALWAYS_INLINE void *getNext(unsigned thingKind, size_t thingSize) {
return lists[thingKind].allocate(thingSize);
}
Cell *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) {
lists[thingKind] = aheader->getFirstFreeSpan();
void *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) {
FreeSpan *list = &lists[thingKind];
*list = aheader->getFirstFreeSpan();
aheader->setAsFullyUsed();
Cell *t = getNext(thingKind, thingSize);
void *t = list->allocate(thingSize);
JS_ASSERT(t);
return t;
}
@ -902,7 +995,7 @@ struct FreeLists {
}
};
extern Cell *
extern void *
RefillFinalizableFreeList(JSContext *cx, unsigned thingKind);
} /* namespace gc */

View File

@ -203,8 +203,8 @@ NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize)
js::gc::RunDebugGC(cx);
#endif
js::gc::Cell *cell = cx->compartment->freeLists.getNext(thingKind, thingSize);
return static_cast<T *>(cell ? cell : js::gc::RefillFinalizableFreeList(cx, thingKind));
void *t = cx->compartment->freeLists.getNext(thingKind, thingSize);
return static_cast<T *>(t ? t : js::gc::RefillFinalizableFreeList(cx, thingKind));
}
inline JSObject *

View File

@ -154,6 +154,19 @@ js::GetBlockChain(JSContext *cx, StackFrame *fp)
JSScript *script = fp->script();
jsbytecode *start = script->code;
/*
* If the debugger asks for the scope chain at a pc where we are about to
* fix it up, advance target past the fixup. See bug 672804.
*/
JSOp op = js_GetOpcode(cx, script, target);
while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 ||
op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 ||
op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN)
{
target += js_CodeSpec[op].length;
op = js_GetOpcode(cx, script, target);
}
JS_ASSERT(target >= start && target < start + script->length);
JSObject *blockChain = NULL;

View File

@ -13212,13 +13212,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex)
OVERFLOW_EXIT, /* abortIfAlwaysExits = */true));
// We're now ready to store
LIns* data_base_ins = w.ldpConstTypedArrayData(slots_ins);
LIns* offset_ins = w.ldiConstTypedArrayByteOffset(slots_ins);
#ifdef NANOJIT_64BIT
LIns* data_ins = w.addp(data_base_ins, w.ui2uq(offset_ins));
#else
LIns* data_ins = w.addp(data_base_ins, offset_ins);
#endif
LIns* data_ins = w.ldpConstTypedArrayData(obj_ins);
LIns* pidx_ins = w.ui2p(idx_ins);
LIns* typed_v_ins = v_ins;
@ -14311,13 +14305,7 @@ TraceRecorder::typedArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_
/* We are now ready to load. Do a different type of load
* depending on what type of thing we're loading. */
LIns* data_base_ins = w.ldpConstTypedArrayData(slots_ins);
LIns* offset_ins = w.ldiConstTypedArrayByteOffset(slots_ins);
#ifdef NANOJIT_64BIT
LIns* data_ins = w.addp(data_base_ins, w.ui2uq(offset_ins));
#else
LIns* data_ins = w.addp(data_base_ins, offset_ins);
#endif
LIns* data_ins = w.ldpConstTypedArrayData(obj_ins);
switch (js::TypedArray::getType(tarray)) {
case js::TypedArray::TYPE_INT8:

View File

@ -925,17 +925,13 @@ class TypedArrayTemplate
do {
obj->setSlot(FIELD_BUFFER, ObjectValue(*bufobj));
/*
* NOTE: unlike the earlier implementation where the 'data' pointed
* directly to the right offset in the ArrayBuffer
* this points to the base of the ArrayBuffer.
* getIndex is modified to get the right index.
*
* This is because on 64 bit systems the jsval.h Private API
* requires pointers stored in jsvals to be two-byte aligned.
* TM and JM both need a few extra instructions to add the offset.
* N.B. The base of the array's data is stored in the object's
* private data rather than a slot, to avoid alignment restrictions
* on private Values.
*/
obj->setSlot(FIELD_DATA, PrivateValue(ArrayBuffer::getDataOffset(bufobj)));
obj->setPrivate(ArrayBuffer::getDataOffset(bufobj) + byteOffset);
} while(0);
obj->setSlot(FIELD_LENGTH, Int32Value(len));
@ -1742,6 +1738,7 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \
{ \
#_typedArray, \
JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
JSCLASS_HAS_PRIVATE | \
JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \
PropertyStub, /* addProperty */ \
PropertyStub, /* delProperty */ \
@ -1757,6 +1754,7 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \
{ \
#_typedArray, \
JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
JSCLASS_HAS_PRIVATE | \
Class::NON_NATIVE, \
PropertyStub, /* addProperty */ \
PropertyStub, /* delProperty */ \

View File

@ -142,12 +142,12 @@ struct JS_FRIEND_API(TypedArray) {
};
enum {
/* Properties of the typed array stored in reserved slots. */
FIELD_LENGTH = 0,
FIELD_BYTEOFFSET,
FIELD_BYTELENGTH,
FIELD_TYPE,
FIELD_BUFFER,
FIELD_DATA,
FIELD_MAX
};
@ -181,8 +181,6 @@ struct JS_FRIEND_API(TypedArray) {
static JSObject * getBuffer(JSObject *obj);
static void * getDataOffset(JSObject *obj);
static void *offsetData(JSObject *obj, uint32 offs);
public:
static bool
isArrayIndex(JSContext *cx, JSObject *obj, jsid id, jsuint *ip = NULL);

View File

@ -84,7 +84,7 @@ TypedArray::getBuffer(JSObject *obj) {
inline void *
TypedArray::getDataOffset(JSObject *obj) {
return (void *)((uint8*)obj->getSlot(FIELD_DATA).toPrivate() + getByteOffset(obj));
return (void *)obj->getPrivate();
}
}
#endif /* jstypedarrayinlines_h */

View File

@ -395,11 +395,12 @@ ForceFrame::enter()
LeaveTrace(context);
JS_ASSERT(context->compartment == target->compartment());
JSCompartment *destination = context->compartment;
JSObject *scopeChain = target->getGlobal();
JS_ASSERT(scopeChain->isNative());
return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame);
return context->stack.pushDummyFrame(context, *scopeChain, frame);
}
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
@ -428,21 +429,8 @@ AutoCompartment::enter()
JS_ASSERT(scopeChain->isNative());
frame.construct();
/*
* Set the compartment eagerly so that pushDummyFrame associates the
* resource allocation request with 'destination' instead of 'origin'.
* (This is important when content has overflowed the stack and chrome
* is preparing to run JS to throw up a slow script dialog.) However,
* if an exception is thrown, we need it to be in origin's compartment
* so be careful to only report after restoring.
*/
context->compartment = destination;
if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) {
context->compartment = origin;
js_ReportOverRecursed(context);
if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref()))
return false;
}
if (context->isExceptionPending())
context->wrapPendingException();

View File

@ -75,7 +75,11 @@ enum JaegerSpewChannel {
void JMCheckLogging();
bool IsJaegerSpewChannelActive(JaegerSpewChannel channel);
#ifdef __GNUC__
void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#else
void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...);
#endif
struct Profiler {
JSInt64 t_start;

View File

@ -2466,12 +2466,10 @@ GetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsi
// Guard on this typed array's clasp.
Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass());
// Get the internal typed array.
masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
// Bounds check.
Jump outOfBounds;
Address typedArrayLength(objReg, sizeof(uint64) * js::TypedArray::FIELD_LENGTH);
masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), typeReg);
Address typedArrayLength(typeReg, sizeof(uint64) * js::TypedArray::FIELD_LENGTH);
typedArrayLength = masm.payloadOf(typedArrayLength);
if (idRemat.isConstant()) {
JS_ASSERT(idRemat.value().toInt32() == v.toInt32());
@ -2481,15 +2479,11 @@ GetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsi
}
// Load the array's packed data vector.
Address data_base(objReg, sizeof(Value) * js::TypedArray::FIELD_DATA);
masm.loadPrivate(data_base, objReg);
masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
JSObject *tarray = js::TypedArray::getTypedArray(obj);
int shift = js::TypedArray::slotWidth(tarray);
int byteOffset = js::TypedArray::getByteOffset(tarray);
masm.addPtr(Imm32(byteOffset), objReg);
if (idRemat.isConstant()) {
int32 index = v.toInt32();
Address addr(objReg, index * shift);
@ -2815,11 +2809,9 @@ SetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, int32 key)
// Guard on this typed array's clasp.
Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass());
// Get the internal typed array.
masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
// Bounds check.
Jump outOfBounds;
masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
Address typedArrayLength(objReg, sizeof(uint64) * js::TypedArray::FIELD_LENGTH);
typedArrayLength = masm.payloadOf(typedArrayLength);
if (hasConstantKey)
@ -2827,12 +2819,12 @@ SetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, int32 key)
else
outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, keyReg);
// Restore |obj|.
masm.rematPayload(StateRemat::FromInt32(objRemat), objReg);
// Load the array's packed data vector.
JSObject *tarray = js::TypedArray::getTypedArray(obj);
int byteOffset = js::TypedArray::getByteOffset(tarray);
Address base_data(objReg, sizeof(uint64) * js::TypedArray::FIELD_DATA);
masm.loadPrivate(base_data, objReg);
masm.addPtr(Imm32(byteOffset), objReg);
masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
int shift = js::TypedArray::slotWidth(obj);
if (hasConstantKey) {

View File

@ -683,11 +683,26 @@ static JSBool
Version(JSContext *cx, uintN argc, jsval *vp)
{
jsval *argv = JS_ARGV(cx, vp);
if (argc > 0 && JSVAL_IS_INT(argv[0]))
*vp = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
else
if (argc == 0 || JSVAL_IS_VOID(argv[0])) {
/* Get version. */
*vp = INT_TO_JSVAL(JS_GetVersion(cx));
return JS_TRUE;
} else {
/* Set version. */
int32 v = -1;
if (JSVAL_IS_INT(argv[0])) {
v = JSVAL_TO_INT(argv[0]);
} else if (JSVAL_IS_DOUBLE(argv[0])) {
jsdouble fv = JSVAL_TO_DOUBLE(argv[0]);
if (int32(fv) == fv)
v = int32(fv);
}
if (v < 0 || v > JSVERSION_LATEST) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "version");
return false;
}
*vp = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(v)));
}
return true;
}
static JSBool

View File

@ -52,3 +52,7 @@ script weakmap.js
script regress-645160.js
script regress-650753.js
script regress-668438.js
require-or(debugMode,skip) script regress-672804-1.js
require-or(debugMode,skip) script regress-672804-2.js
require-or(debugMode,skip) script regress-672804-3.js
script regress-677924.js

View File

@ -0,0 +1,12 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var a = 0;
function f() {
let (a = let (x = 1) x) {}
}
trap(f, 3, 'assertEq(evalInFrame(1, "a"), 0)');
f();
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,12 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var a = 0;
function f() {
let (a = let (x = 1) x) {}
}
trap(f, 4, 'assertEq(evalInFrame(1, "a"), 0)');
f();
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,11 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var e = [], x = {b: []};
function f() {
let (a = [[] for (x in e)], {b: []} = x) {}
}
trap(f, 4, '');
f();
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,14 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
try {
version(4096); // don't assert
} catch (exc) {
}
try {
version(-1); // don't assert
} catch (exc) {
}
reportCompare(0, 0, 'ok');

View File

@ -555,19 +555,9 @@ class Writer
"typedArrayByteOffset");
}
nj::LIns *ldpConstTypedArrayData(nj::LIns *array) const {
//return name(lir->insLoad(nj::LIR_ldp, array, sizeof(Value) * js::TypedArray::FIELD_DATA + sPayloadOffset, ACCSET_TARRAY,
//nj::LOAD_CONST),
//"typedElems");
uint32 offset = sizeof(Value) * js::TypedArray::FIELD_DATA + sPayloadOffset;
#if JS_BITS_PER_WORD == 32
return name(lir->insLoad(nj::LIR_ldi, array, offset, ACCSET_TARRAY, nj::LOAD_CONST), "typedArrayData");
#elif JS_BITS_PER_WORD == 64
/* N.B. On 64-bit, privatized value are encoded differently from other pointers. */
nj::LIns *v_ins = lir->insLoad(nj::LIR_ldq, array, offset,
ACCSET_TARRAY, nj::LOAD_CONST);
return name(lshqN(v_ins, 1), "typedArrayData");
#endif
nj::LIns *ldpConstTypedArrayData(nj::LIns *obj) const {
uint32 offset = offsetof(JSObject, privateData);
return name(lir->insLoad(nj::LIR_ldp, obj, offset, ACCSET_TARRAY, nj::LOAD_CONST), "typedArrayData");
}
nj::LIns *ldc2iTypedArrayElement(nj::LIns *elems, nj::LIns *index) const {

View File

@ -388,7 +388,8 @@ StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx)
STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
JS_ALWAYS_INLINE bool
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
JSCompartment *dest) const
{
assertInvariants();
JS_ASSERT(from >= firstUnused());
@ -396,10 +397,16 @@ StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptr
JS_ASSERT(from <= commitEnd_);
#endif
if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
return ensureSpaceSlow(cx, report, from, nvals);
return ensureSpaceSlow(cx, report, from, nvals, dest);
return true;
}
bool
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
{
return ensureSpace(cx, report, from, nvals, cx->compartment);
}
inline Value *
StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
{

View File

@ -414,13 +414,13 @@ StackSpace::mark(JSTracer *trc)
}
JS_FRIEND_API(bool)
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
JSCompartment *dest) const
{
assertInvariants();
bool trusted = !cx->compartment ||
cx->compartment->principals == cx->runtime->trustedPrincipals();
JS_ASSERT_IF(dest, cx);
bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
Value *end = trusted ? trustedEnd_ : defaultEnd_;
/*
@ -548,17 +548,17 @@ ContextStack::containsSlow(const StackFrame *target) const
*/
Value *
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg)
MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
{
Value *firstUnused = space().firstUnused();
if (onTop() && extend) {
if (!space().ensureSpace(cx, report, firstUnused, nvars))
if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
return NULL;
return firstUnused;
}
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
return NULL;
FrameRegs *regs;
@ -577,6 +577,13 @@ ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
return seg_->slotsBegin();
}
Value *
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg)
{
return ensureOnTop(cx, report, nvars, extend, pushedSeg, cx->compartment);
}
void
ContextStack::popSegment()
{
@ -696,11 +703,12 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
}
bool
ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
DummyFrameGuard *dfg)
ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
{
JSCompartment *dest = scopeChain.compartment();
uintN nvars = VALUES_PER_STACK_FRAME;
Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_);
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
if (!firstUnused)
return NULL;
@ -708,6 +716,7 @@ ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &s
fp->initDummyFrame(cx, scopeChain);
dfg->regs_.initDummyFrame(*fp);
cx->compartment = dest;
dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
dfg->setPushed(*this);
@ -790,24 +799,11 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
bool
ContextStack::saveFrameChain()
{
/*
* The StackSpace uses the context's current compartment to determine
* whether to allow access to the privileged end-of-stack buffer.
* However, we always want saveFrameChain to have access to this privileged
* buffer since it gets used to prepare calling trusted JS. To force this,
* we clear the current compartment (which is interpreted by ensureSpace as
* 'trusted') and either restore it on OOM or let resetCompartment()
* clobber it.
*/
JSCompartment *original = cx_->compartment;
cx_->compartment = NULL;
JSCompartment *dest = NULL;
bool pushedSeg;
if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) {
cx_->compartment = original;
js_ReportOverRecursed(cx_);
if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
return false;
}
JS_ASSERT(pushedSeg);
JS_ASSERT(!hasfp());

View File

@ -44,6 +44,7 @@
#include "jsfun.h"
struct JSContext;
struct JSCompartment;
namespace js {
@ -1329,10 +1330,15 @@ class StackSpace
friend class ContextStack;
friend class StackFrame;
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals,
JSCompartment *dest) const;
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
Value *from, ptrdiff_t nvals,
JSCompartment *dest) const;
StackSegment &findContainingSegment(const StackFrame *target) const;
public:
@ -1420,6 +1426,9 @@ class ContextStack
/* Implementation details of push* public interface. */
StackSegment *pushSegment(JSContext *cx);
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg,
JSCompartment *dest);
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg);
@ -1502,9 +1511,16 @@ class ContextStack
*/
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
/* Pushes a "dummy" frame; should be removed one day. */
bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
DummyFrameGuard *dfg);
/*
* When changing the compartment of a cx, it is necessary to immediately
* change the scope chain to a global in the right compartment since any
* amount of general VM code can run before the first scripted frame is
* pushed (if at all). This is currently and hackily accomplished by
* pushing a "dummy frame" with the correct scope chain. On success, this
* function will change the compartment to 'scopeChain.compartment()' and
* push a dummy frame for 'scopeChain'. On failure, nothing is changed.
*/
bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
/*
* An "inline frame" may only be pushed from within the top, active

View File

@ -262,12 +262,7 @@ nsLayoutStatics::Initialize()
NS_SealStaticAtomTable();
// TODO: DOM_MEMORY_REPORTER should not be defined in a regular build for the
// moment. This protection will be removed when bug 663271 will be close enough
// to a shippable state.
#ifdef DOM_MEMORY_REPORTER
nsDOMMemoryReporter::Init();
#endif
return NS_OK;
}

View File

@ -1495,7 +1495,7 @@ nsLineLayout::VerticalAlignLine()
if (vAlign.GetUnit() != eStyleUnit_Enumerated ||
vAlign.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
const nscoord offset = baselineY - (pfd->mBounds.y);
const nscoord offset = baselineY - pfd->mBounds.y;
f->Properties().Set(nsIFrame::LineBaselineOffset(),
NS_INT32_TO_PTR(offset));
}

View File

@ -4279,7 +4279,12 @@ nsTextFrame::GetTextDecorations(nsPresContext* aPresContext,
bool nearestBlockFound = false;
for (nsIFrame* f = this, *fParent; f; f = fParent) {
for (nsIFrame* f = this, *fChild = nsnull;
f;
fChild = f,
f = nsLayoutUtils::GetParentOrPlaceholderFor(
aPresContext->FrameManager(), f))
{
nsStyleContext *const context = f->GetStyleContext();
if (!context->HasTextDecorationLines()) {
break;
@ -4297,10 +4302,7 @@ nsTextFrame::GetTextDecorations(nsPresContext* aPresContext,
nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
}
fParent = nsLayoutUtils::GetParentOrPlaceholderFor(
aPresContext->FrameManager(), f);
const bool firstBlock = !nearestBlockFound &&
nsLayoutUtils::GetAsBlock(fParent);
const bool firstBlock = !nearestBlockFound && nsLayoutUtils::GetAsBlock(f);
// Not updating positions once we hit a parent block is equivalent to
// the CSS 2.1 spec that blocks should propagate decorations down to their
@ -4308,19 +4310,28 @@ nsTextFrame::GetTextDecorations(nsPresContext* aPresContext,
// However, if we're vertically aligned within a block, then we need to
// recover the right baseline from the line by querying the FrameProperty
// that should be set (see nsLineLayout::VerticalAlignLine).
if (firstBlock &&
(styleText->mVerticalAlign.GetUnit() != eStyleUnit_Enumerated ||
styleText->mVerticalAlign.GetIntValue() !=
NS_STYLE_VERTICAL_ALIGN_BASELINE)) {
baselineOffset = frameTopOffset -
NS_PTR_TO_INT32(f->Properties().Get(nsIFrame::LineBaselineOffset()));
if (firstBlock) {
// At this point, fChild can't be null since TextFrames can't be blocks
const nsStyleCoord& vAlign =
fChild->GetStyleContext()->GetStyleTextReset()->mVerticalAlign;
if (vAlign.GetUnit() != eStyleUnit_Enumerated ||
vAlign.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE)
{
// Since offset is the offset in the child's coordinate space, we have
// to undo the accumulation to bring the transform out of the block's
// coordinate space
baselineOffset =
frameTopOffset - (fChild->GetRect().y - fChild->GetRelativeOffset().y)
- NS_PTR_TO_INT32(
fChild->Properties().Get(nsIFrame::LineBaselineOffset()));
}
}
else if (!nearestBlockFound) {
baselineOffset = frameTopOffset - f->GetBaseline();
}
nearestBlockFound = nearestBlockFound || firstBlock;
frameTopOffset += f->GetRect().Y() - f->GetRelativeOffset().y;
frameTopOffset += f->GetRect().y - f->GetRelativeOffset().y;
const PRUint8 style = styleText->GetDecorationStyle();
// Accumulate only elements that have decorations with a genuine style

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>text-decoration alignment</title>
<meta charset=UTF-8>
<style>
span.block { position: absolute; top: 0; left: 0; }
span.dec { text-decoration: underline }
span.hide { color: transparent }
</style>
</head>
<body>
<p style="position: relative">
<span class="block" style="margin-top: 16px"><span class="hide"></span><span class="dec" style="color:black ">x<span class="hide">xx</span></span></span>
<span class="block" style="margin-top: 08px"><span class="hide">x</span><span class="dec" style="color:fuchsia">x<span class="hide">x</span></span></span>
<span class="block" style="margin-top: 00px"><span class="hide">xx</span><span class="dec" style="color:aqua ">x<span class="hide"></span></span></span>
</p>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>text-decoration alignment</title>
<meta charset=UTF-8>
<style>
p, span { text-decoration: underline }
span { vertical-align: 8px }
p > span { color: fuchsia }
p > span > span { color: aqua }
em { font-style: normal; font-size: 5em }
</style>
</head>
<body>
<p>x<span>x<span>x</span></span></p>
</body>
</html>

View File

@ -96,3 +96,4 @@ fails == underline-block-propagation-2-quirks.html underline-block-propagation-2
== decoration-css21-block.html decoration-css21-block-ref.html
!= inline-baseline-almost-standards.html inline-baseline-almost-standards-ref.html
!= inline-baseline-quirks.html inline-baseline-quirks-ref.html
== 676538-1.html 676538-1-ref.html

View File

@ -256,8 +256,13 @@ let WebNavigation = {
if (aEntry.docshellID)
shEntry.docshellID = aEntry.docshellID;
if (aEntry.stateData)
shEntry.stateData = aEntry.stateData;
if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
shEntry.stateData =
Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion);
}
if (aEntry.scroll) {
let scrollPos = aEntry.scroll.split(",");
@ -266,23 +271,15 @@ let WebNavigation = {
}
if (aEntry.docIdentifier) {
// Get a new document identifier for this entry to ensure that history
// entries after a session restore are considered to have different
// documents from the history entries before the session restore.
// Document identifiers are 64-bit ints, so JS will loose precision and
// start assigning all entries the same doc identifier if these ever get
// large enough.
//
// It's a potential security issue if document identifiers aren't
// globally unique, but shEntry.setUniqueDocIdentifier() below guarantees
// that we won't re-use a doc identifier within a given instance of the
// application.
let ident = aDocIdentMap[aEntry.docIdentifier];
if (!ident) {
shEntry.setUniqueDocIdentifier();
aDocIdentMap[aEntry.docIdentifier] = shEntry.docIdentifier;
// If we have a serialized document identifier, try to find an SHEntry
// which matches that doc identifier and adopt that SHEntry's
// BFCacheEntry. If we don't find a match, insert shEntry as the match
// for the document identifier.
let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
if (!matchingEntry) {
aDocIdentMap[aEntry.docIdentifier] = shEntry;
} else {
shEntry.docIdentifier = ident;
shEntry.adoptBFCacheEntry(matchingEntry);
}
}
@ -367,11 +364,12 @@ let WebNavigation = {
} catch (e) { dump(e); }
}
if (aEntry.docIdentifier)
entry.docIdentifier = aEntry.docIdentifier;
entry.docIdentifier = aEntry.BFCacheEntry.ID;
if (aEntry.stateData)
entry.stateData = aEntry.stateData;
if (aEntry.stateData != null) {
entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
entry.structuredCloneVersion = aEntry.stateData.formatVersion;
}
if (!(aEntry instanceof Ci.nsISHContainer))
return entry;

View File

@ -355,10 +355,12 @@ var Browser = {
// Initial window resizes call functions that assume a tab is in the tab list
// and restored tabs are added too late. We add a dummy to to satisfy the resize
// code and then remove the dummy after the session has been restored.
let dummy = this.addTab("about:blank");
let dummy = this.addTab("about:blank", true);
let dummyCleanup = {
observe: function() {
observe: function(aSubject, aTopic, aData) {
Services.obs.removeObserver(dummyCleanup, "sessionstore-windows-restored");
if (aData == "fail")
Browser.addTab(commandURL || Browser.getHomePage(), true);
dummy.chromeTab.ignoreUndo = true;
Browser.closeTab(dummy, { forceClose: true });
}

View File

@ -400,7 +400,7 @@ let Content = {
switch (aMessage.name) {
case "Browser:ContextCommand": {
let wrappedTarget = elementFromPoint(x, y);
if (!wrappedTarget)
if (!wrappedTarget || !(wrappedTarget instanceof Ci.nsIDOMNSEditableElement))
break;
let target = wrappedTarget.QueryInterface(Ci.nsIDOMNSEditableElement);
if (!target)

View File

@ -707,14 +707,16 @@ SessionStore.prototype = {
},
restoreLastSession: function ss_restoreLastSession(aBringToFront) {
// The previous session data has already been renamed to the backup file
if (!this._sessionFileBackup.exists())
return;
let self = this;
function notifyObservers() {
function notifyObservers(aMessage) {
self._clearCache();
Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
Services.obs.notifyObservers(null, "sessionstore-windows-restored", aMessage || "");
}
// The previous session data has already been renamed to the backup file
if (!this._sessionFileBackup.exists()) {
notifyObservers("fail")
return;
}
try {
@ -723,7 +725,7 @@ SessionStore.prototype = {
NetUtil.asyncFetch(channel, function(aStream, aResult) {
if (!Components.isSuccessCode(aResult)) {
Cu.reportError("SessionStore: Could not read from sessionstore.bak file");
notifyObservers();
notifyObservers("fail");
return;
}
@ -742,14 +744,17 @@ SessionStore.prototype = {
}
if (!data || data.windows.length == 0) {
notifyObservers();
notifyObservers("fail");
return;
}
let window = Services.wm.getMostRecentWindow("navigator:browser");
let selected = data.windows[0].selected;
let tabs = data.windows[0].tabs;
let selected = data.windows[0].selected;
if (selected > tabs.length) // Clamp the selected index if it's bogus
selected = 1;
for (let i=0; i<tabs.length; i++) {
let tabData = tabs[i];
@ -795,7 +800,7 @@ SessionStore.prototype = {
});
} catch (ex) {
Cu.reportError("SessionStore: Could not read from sessionstore.bak file: " + ex);
notifyObservers();
notifyObservers("fail");
}
}
};

View File

@ -56,7 +56,7 @@ richlistitem[default="false"] .preferences-title {
font-weight: bold;
}
richlistitem .prefvalue {
richlistitem .preferences-value {
min-width: 200px;
pointer-events: none;
-moz-box-flex: 4;

View File

@ -3339,6 +3339,73 @@ cancel_and_failure(nsNSSSocketInfo* infoObject)
return SECFailure;
}
class nsIsStsHostRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
nsIsStsHostRunnable(const nsCOMPtr<nsIStrictTransportSecurityService> &stss)
: stss(stss), stsEnabled(PR_FALSE), nsrv(NS_ERROR_UNEXPECTED)
{}
nsXPIDLCString hostName;
nsresult GetResult(PRBool &b) const { b = stsEnabled; return nsrv; }
private:
nsCOMPtr<nsIStrictTransportSecurityService> stss;
PRBool stsEnabled;
nsresult nsrv;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsIsStsHostRunnable,
nsIRunnable)
NS_IMETHODIMP nsIsStsHostRunnable::Run()
{
nsrv = stss->IsStsHost(hostName, &stsEnabled);
return NS_OK;
}
class nsNotifyCertProblemRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
nsNotifyCertProblemRunnable(nsIInterfaceRequestor *cb,
nsIInterfaceRequestor *csi,
nsSSLStatus* status,
const nsCString &hostWithPortString)
: cb(cb),
csi(csi),
status(status),
hostWithPortString(hostWithPortString),
suppressMessage(PR_FALSE)
{}
PRBool GetSuppressMessage() { return suppressMessage; }
private:
nsIInterfaceRequestor* cb;
nsIInterfaceRequestor* csi;
nsSSLStatus* status;
const nsCString& hostWithPortString;
PRBool suppressMessage;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsNotifyCertProblemRunnable,
nsIRunnable)
NS_IMETHODIMP nsNotifyCertProblemRunnable::Run()
{
nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
if (bcl)
bcl->NotifyCertProblem(csi, status, hostWithPortString, &suppressMessage);
return NS_OK;
}
static SECStatus
nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
{
@ -3522,21 +3589,25 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
nsCOMPtr<nsIStrictTransportSecurityService> stss
= do_GetService(NS_STSSERVICE_CONTRACTID);
nsCOMPtr<nsIStrictTransportSecurityService> proxied_stss;
nsrv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIStrictTransportSecurityService),
stss, NS_PROXY_SYNC,
getter_AddRefs(proxied_stss));
NS_ENSURE_SUCCESS(nsrv, SECFailure);
nsCOMPtr<nsIsStsHostRunnable> runnable(new nsIsStsHostRunnable(stss));
if (!runnable)
return SECFailure;
// now grab the host name to pass to the STS Service
nsXPIDLCString hostName;
nsrv = infoObject->GetHostName(getter_Copies(hostName));
nsrv = infoObject->GetHostName(getter_Copies(runnable->hostName));
NS_ENSURE_SUCCESS(nsrv, SECFailure);
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
if (!mainThread)
return SECFailure;
// Dispatch SYNC since the result is used below
nsrv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(nsrv, SECFailure);
PRBool strictTransportSecurityEnabled;
nsrv = proxied_stss->IsStsHost(hostName, &strictTransportSecurityEnabled);
nsrv = runnable->GetResult(strictTransportSecurityEnabled);
NS_ENSURE_SUCCESS(nsrv, SECFailure);
if (!strictTransportSecurityEnabled) {
@ -3576,33 +3647,23 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
// giving the caller a chance to suppress the error messages.
PRBool suppressMessage = PR_FALSE;
nsresult rv;
// Try to get a nsIBadCertListener2 implementation from the socket consumer.
nsCOMPtr<nsIInterfaceRequestor> cb;
infoObject->GetNotificationCallbacks(getter_AddRefs(cb));
if (cb) {
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIInterfaceRequestor),
cb,
NS_PROXY_SYNC,
getter_AddRefs(callbacks));
nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(callbacks);
if (bcl) {
nsCOMPtr<nsIBadCertListener2> proxy_bcl;
NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIBadCertListener2),
bcl,
NS_PROXY_SYNC,
getter_AddRefs(proxy_bcl));
if (proxy_bcl) {
nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
rv = proxy_bcl->NotifyCertProblem(csi, status, hostWithPortString,
&suppressMessage);
}
}
nsCOMPtr<nsNotifyCertProblemRunnable> runnable(
new nsNotifyCertProblemRunnable(cb, csi, status, hostWithPortString));
if (!runnable)
return SECFailure;
// Dispatch SYNC since the result is used below
nsrv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(nsrv, SECFailure);
suppressMessage = runnable->GetSuppressMessage();
}
nsCOMPtr<nsIRecentBadCertsService> recentBadCertsService =

View File

@ -135,6 +135,36 @@ function sendHeapMinNotifications()
sendHeapMinNotificationsInner();
}
function Reporter(aPath, aKind, aUnits, aAmount, aDescription)
{
this._path = aPath;
this._kind = aKind;
this._units = aUnits;
this._amount = aAmount;
this._description = aDescription;
// this._nMerged is only defined if > 1
// this._done is defined when getBytes is called
}
Reporter.prototype = {
// Sum the values (accounting for possible kUnknown amounts), and mark |this|
// as a dup. We mark dups because it's useful to know when a reporter is
// duplicated; it might be worth investigating and splitting up to have
// non-duplicated names.
merge: function(r) {
if (this._amount !== kUnknown && r._amount !== kUnknown) {
this._amount += r._amount;
} else if (this._amount === kUnknown && r._amount !== kUnknown) {
this._amount = r._amount;
}
this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
},
treeNameMatches: function(aTreeName) {
return this._path.slice(0, aTreeName.length) === aTreeName;
}
};
function getReportersByProcess()
{
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
@ -142,18 +172,8 @@ function getReportersByProcess()
// Process each memory reporter:
// - Make a copy of it into a sub-table indexed by its process. Each copy
// looks like this:
//
// interface Reporter {
// _path: string;
// _kind: number;
// _units: number;
// _amount: number;
// _description: string;
// _nMerged: number; (only defined if >= 2)
// }
//
// After this point we never use the original memory reporter again.
// is a Reporter object. After this point we never use the original memory
// reporter again.
//
// - Note that copying rOrig.amount (which calls a C++ function under the
// IDL covers) to r._amount for every reporter now means that the
@ -165,30 +185,16 @@ function getReportersByProcess()
function addReporter(aProcess, aPath, aKind, aUnits, aAmount, aDescription)
{
var process = aProcess === "" ? "Main" : aProcess;
var r = {
_path: aPath,
_kind: aKind,
_units: aUnits,
_amount: aAmount,
_description: aDescription
};
var r = new Reporter(aPath, aKind, aUnits, aAmount, aDescription);
if (!reportersByProcess[process]) {
reportersByProcess[process] = {};
}
var reporters = reportersByProcess[process];
var reporter = reporters[r._path];
if (reporter) {
// Already an entry; must be a duplicated reporter. This can
// happen legitimately. Sum the values (accounting for possible kUnknown
// amounts), and mark the reporter as a dup. We mark dups because it's
// useful to know when a reporter is duplicated; it might be worth
// investigating and splitting up to have non-duplicated names.
if (reporter._amount !== kUnknown && r._amount !== kUnknown) {
reporter._amount += r._amount;
} else if (reporter._amount === kUnknown && r._amount !== kUnknown) {
reporter._amount = r._amount;
}
reporter._nMerged = reporter._nMerged ? reporter._nMerged + 1 : 2;
// Already an entry; must be a duplicated reporter. This can happen
// legitimately. Merge them.
reporter.merge(r);
} else {
reporters[r._path] = r;
}
@ -203,8 +209,8 @@ function getReportersByProcess()
}
var e = mgr.enumerateMultiReporters();
while (e.hasMoreElements()) {
var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
r.collectReports(addReporter, null);
var mrOrig = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
mrOrig.collectReports(addReporter, null);
}
return reportersByProcess;
@ -266,68 +272,63 @@ function update()
content.appendChild(div);
}
// Compare two 'explicit' memory reporter nodes.
function cmpExplicitReporters(a, b)
// There are two kinds of TreeNode. Those that correspond to Reporters
// have more properties. The remainder are just scaffolding nodes for the
// tree, whose values are derived from their children.
function TreeNode(aName)
{
assert(a._units === undefined && b._units === undefined,
"'explicit' tree nodes must not have _units defined");
// Nb: _units is not needed, it's always UNITS_BYTES.
this._name = aName;
this._kids = [];
// All TreeNodes have these properties added later:
// - _amount (which is never |kUnknown|)
// - _description
//
// TreeNodes corresponding to Reporters have these properties added later:
// - _kind
// - _nMerged (if > 1)
// - _hasProblem (only defined if true)
}
TreeNode.prototype = {
findKid: function(aName) {
for (var i = 0; i < this._kids.length; i++) {
if (this._kids[i]._name === aName) {
return this._kids[i];
}
}
return undefined;
},
toString: function() {
return formatBytes(this._amount);
}
};
TreeNode.compare = function(a, b) {
return b._amount - a._amount;
};
// Compare two memory reporter nodes from the 'other measurements' list.
function cmpOtherReporters(a, b)
{
return a._path < b._path ? -1 :
a._path > b._path ? 1 :
0;
};
function findKid(aName, aKids)
{
for (var i = 0; i < aKids.length; i++) {
if (aKids[i]._name === aName) {
return aKids[i];
}
}
return undefined;
}
/**
* From a list of memory reporters, builds a tree that mirrors the tree
* structure that will be shown as output.
*
* @param aReporters
* The list of memory reporters.
* @return The built tree. The tree nodes have this structure:
* interface Node {
* _name: string;
* _kind: number;
* _amount: number; (non-negative or 'kUnknown')
* _description: string;
* _kids: [Node];
* _hasReporter: boolean; (only defined if 'true')
* _hasProblem: boolean; (only defined if 'true')
* _nMerged: number; (only defined if >= 2)
* }
* _units isn't needed because it's always UNITS_BYTES for 'explicit'
* reporters.
* The table of Reporters, indexed by path.
* @return The built tree.
*/
function buildTree(aReporters)
{
const treeName = "explicit";
// We want to process all reporters that begin with 'treeName'. First we
// build the tree but only fill in '_name', '_kind', '_kids', maybe
// '_hasReporter' and maybe '_nMerged'. This is done top-down from the
// reporters.
var t = {
_name: "falseRoot",
_kind: KIND_OTHER,
_kids: []
};
// We want to process all reporters that begin with |treeName|. First we
// build the tree but only fill the properties that we can with a top-down
// traversal.
var t = new TreeNode("falseRoot");
for (var path in aReporters) {
// Add any missing nodes in the tree implied by the path.
var r = aReporters[path];
if (r._path.slice(0, treeName.length) === treeName) {
if (r.treeNameMatches(treeName)) {
assert(r._kind === KIND_HEAP || r._kind === KIND_NONHEAP,
"reporters in the tree must have KIND_HEAP or KIND_NONHEAP");
assert(r._units === UNITS_BYTES);
@ -335,21 +336,17 @@ function buildTree(aReporters)
var u = t;
for (var i = 0; i < names.length; i++) {
var name = names[i];
var uMatch = findKid(name, u._kids);
var uMatch = u.findKid(name);
if (uMatch) {
u = uMatch;
} else {
var v = {
_name: name,
_kind: KIND_OTHER,
_kids: []
};
var v = new TreeNode(name);
u._kids.push(v);
u = v;
}
}
// Fill in extra details from the Reporter.
u._kind = r._kind;
u._hasReporter = true;
if (r._nMerged) {
u._nMerged = r._nMerged;
}
@ -359,14 +356,14 @@ function buildTree(aReporters)
// treeName at the root.
t = t._kids[0];
// Next, fill in '_description' and '_amount', and maybe '_hasProblem'
// for each node. This is done bottom-up because for most non-leaf nodes
// '_amount' and '_description' are determined from the child nodes.
// Next, fill in the remaining properties bottom-up.
// Note that this function never returns kUnknown.
function fillInTree(aT, aPrepath)
{
var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
if (aT._kids.length === 0) {
// Leaf node. Must have a reporter.
assert(aT._kind !== undefined);
aT._description = getDescription(aReporters, path);
var amount = getBytes(aReporters, path);
if (amount !== kUnknown) {
@ -380,23 +377,18 @@ function buildTree(aReporters)
var childrenBytes = 0;
for (var i = 0; i < aT._kids.length; i++) {
// Allow for kUnknown, treat it like 0.
var b = fillInTree(aT._kids[i], path);
childrenBytes += (b === kUnknown ? 0 : b);
childrenBytes += fillInTree(aT._kids[i], path);
}
if (aT._hasReporter === true) {
if (aT._kind !== undefined) {
aT._description = getDescription(aReporters, path);
var amount = getBytes(aReporters, path);
if (amount !== kUnknown) {
// Non-leaf node with its own reporter. Use the reporter and add
// an "other" child node.
aT._amount = amount;
var other = {
_name: "other",
_kind: KIND_OTHER,
_description: "All unclassified " + aT._name + " memory.",
_amount: aT._amount - childrenBytes,
_kids: []
};
var other = new TreeNode("other");
other._description = "All unclassified " + aT._name + " memory.",
other._amount = aT._amount - childrenBytes,
aT._kids.push(other);
} else {
// Non-leaf node with a reporter that returns kUnknown.
@ -411,6 +403,7 @@ function buildTree(aReporters)
aT._description = "The sum of all entries below '" + aT._name + "'.";
}
}
assert(aT._amount !== kUnknown);
return aT._amount;
}
fillInTree(t, "");
@ -442,17 +435,20 @@ function buildTree(aReporters)
unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t);
hasProblem = false;
}
var heapUnclassified = {
_name: "heap-unclassified",
_kind: KIND_HEAP,
_description:
var heapUnclassified = new TreeNode("heap-unclassified");
// This kindToString() ensures the "(Heap)" prefix is set without having to
// set the _kind property, which would mean that there is a corresponding
// Reporter for this TreeNode (which isn't true).
heapUnclassified._description =
kindToString(KIND_HEAP) +
"Memory not classified by a more specific reporter. This includes " +
"waste due to internal fragmentation in the heap allocator (caused " +
"when the allocator rounds up request sizes).",
_amount: unknownHeapUsedBytes,
_hasProblem: hasProblem,
_kids: []
"when the allocator rounds up request sizes).";
heapUnclassified._amount = unknownHeapUsedBytes;
if (hasProblem) {
heapUnclassified._hasProblem = true;
}
t._kids.push(heapUnclassified);
t._amount += unknownHeapUsedBytes;
@ -460,13 +456,13 @@ function buildTree(aReporters)
}
/**
* Sort all kid nodes from largest to smallest and aggregate
* insignificant nodes.
* Sort all kid nodes from largest to smallest and aggregate insignificant
* nodes.
*
* @param aTotalBytes
* The size of the tree's root node
* The size of the tree's root node.
* @param aT
* The tree
* The tree.
*/
function filterTree(aTotalBytes, aT)
{
@ -479,7 +475,7 @@ function filterTree(aTotalBytes, aT)
(100 * aBytes / aTotalBytes) < omitThresholdPerc;
}
aT._kids.sort(cmpExplicitReporters);
aT._kids.sort(TreeNode.compare);
for (var i = 0; i < aT._kids.length; i++) {
if (shouldOmit(aT._kids[i]._amount)) {
@ -493,21 +489,18 @@ function filterTree(aTotalBytes, aT)
}
aT._kids.splice(i0);
var n = i - i0;
var rSub = {
_name: "(" + n + " omitted)",
_kind: KIND_OTHER,
_amount: aggBytes,
_description: n + " sub-trees that were below the " +
omitThresholdPerc + "% significance threshold. " +
"Click 'More verbose' at the bottom of this page " +
"to see them.",
_kids: []
};
// Add the "omitted" sub-tree at the end and then resort, because the
// sum of the omitted sub-trees may be larger than some of the
// shown sub-trees.
var rSub = new TreeNode("(" + n + " omitted)");
rSub._amount = aggBytes;
rSub._description =
n + " sub-trees that were below the " + omitThresholdPerc +
"% significance threshold. Click 'More verbose' at the bottom of " +
"this page to see them.";
// Add the "omitted" sub-tree at the end and then re-sort, because the
// sum of the omitted sub-trees may be larger than some of the shown
// sub-trees.
aT._kids[i0] = rSub;
aT._kids.sort(cmpExplicitReporters);
aT._kids.sort(TreeNode.compare);
break;
}
filterTree(aTotalBytes, aT._kids[i]);
@ -518,10 +511,10 @@ function filterTree(aTotalBytes, aT)
* Generates the text for a single process.
*
* @param aProcess
* The name of the process
* The name of the process.
* @param aReporters
* Table of reporters for this process, indexed by _path
* @return The generated text
* Table of Reporters for this process, indexed by _path.
* @return The generated text.
*/
function genProcessText(aProcess, aReporters)
{
@ -537,34 +530,12 @@ function genProcessText(aProcess, aReporters)
return text;
}
/**
* Returns the reporter's amount formatted as a human-readable string (with
* units, if applicable).
*
* @param aReporter
* The reporter whose usage we're formatting
* @return The reporter's amount formatted as a human-readable string
*/
function formatReporterAmount(aReporter)
{
switch(aReporter._units) {
case UNITS_BYTES:
return formatBytes(aReporter._amount);
case UNITS_COUNT:
case UNITS_COUNT_CUMULATIVE:
return formatInt(aReporter._amount);
case UNITS_PERCENTAGE:
return formatPercentage(aReporter._amount);
default: return "(???)"
}
}
/**
* Formats an int as a human-readable string.
*
* @param aN
* The integer to format
* @return A human-readable string representing the int
* The integer to format.
* @return A human-readable string representing the int.
*/
function formatInt(aN)
{
@ -597,8 +568,8 @@ function formatInt(aN)
* Converts a byte count to an appropriate string representation.
*
* @param aBytes
* The byte count
* @return The string representation
* The byte count.
* @return The string representation.
*/
function formatBytes(aBytes)
{
@ -619,7 +590,7 @@ function formatBytes(aBytes)
* Converts a percentage to an appropriate string representation.
*
* @param aPerc100x
* The percentage, multiplied by 100 (see nsIMemoryReporter)
* The percentage, multiplied by 100 (see nsIMemoryReporter).
* @return The string representation
*/
function formatPercentage(aPerc100x)
@ -628,15 +599,15 @@ function formatPercentage(aPerc100x)
}
/**
* Right-justifies a string in a field of a given width, padding as necessary
* Right-justifies a string in a field of a given width, padding as necessary.
*
* @param aS
* The string
* The string.
* @param aN
* The field width
* The field width.
* @param aC
* The char used to pad
* @return The string representation
* The char used to pad.
* @return The string representation.
*/
function pad(aS, aN, aC)
{
@ -649,46 +620,41 @@ function pad(aS, aN, aC)
}
/**
* Gets the byte count for a particular memory reporter and sets its _done
* Gets the byte count for a particular Reporter and sets its _done
* property.
*
* @param aReporters
* Table of reporters for this process, indexed by _path
* Table of Reporters for this process, indexed by _path.
* @param aPath
* The path of the memory reporter
* The path of the R.
* @param aDoNotMark
* If set, the _done property is not set.
* @return The byte count
* If true, the _done property is not set.
* @return The byte count.
*/
function getBytes(aReporters, aPath, aDoNotMark)
{
var r = aReporters[aPath];
if (r) {
var bytes = r._amount;
if (!aDoNotMark) {
r._done = true;
}
return bytes;
assert(r, "getBytes: no such Reporter: " + aPath);
if (!aDoNotMark) {
r._done = true;
}
// Nb: this should never occur; all paths have been extracted from
// the original list of reporters and so the lookup should succeed. Return
// an obviously wrong number that will likely be noticed.
return -2 * 1024 * 1024;
return r._amount;
}
/**
* Gets the description for a particular memory reporter.
* Gets the description for a particular Reporter.
*
* @param aReporters
* Table of reporters for this process, indexed by _path
* Table of Reporters for this process, indexed by _path.
* @param aPath
* The path of the memory reporter
* @return The description
* The path of the Reporter.
* @return The description.
*/
function getDescription(aReporters, aPath)
{
var r = aReporters[aPath];
return r ? r._description : "???";
assert(r, "getDescription: no such Reporter: " + aPath);
return r._description;
}
function genMrValueText(aValue)
@ -701,8 +667,9 @@ function kindToString(aKind)
switch (aKind) {
case KIND_NONHEAP: return "(Non-heap) ";
case KIND_HEAP: return "(Heap) ";
case KIND_OTHER: return "";
default: return "(???) ";
case KIND_OTHER:
case undefined: return "";
default: assert(false, "bad kind in kindToString");
}
}
@ -770,29 +737,29 @@ function genMrNameText(aKind, aDesc, aName, aHasProblem, aNMerged)
* Generates the text for the tree, including its heading.
*
* @param aT
* The tree
* @return The generated text
* The tree.
* @return The generated text.
*/
function genTreeText(aT)
{
var treeBytes = aT._amount;
var treeBytesLength = formatBytes(treeBytes).length;
var rootStringLength = aT.toString().length;
/**
* Generates the text for a particular tree, without a heading.
*
* @param aT
* The tree
* The tree.
* @param aIndentGuide
* Records what indentation is required for this tree. It has one
* entry per level of indentation. For each entry, ._isLastKid
* records whether the node in question is the last child, and
* ._depth records how many chars of indentation are required.
* @param aParentBytesLength
* The length of the formatted byte count of the top node in the tree
* @return The generated text
* @param aParentStringLength
* The length of the formatted byte count of the top node in the tree.
* @return The generated text.
*/
function genTreeText2(aT, aIndentGuide, aParentBytesLength)
function genTreeText2(aT, aIndentGuide, aParentStringLength)
{
function repeatStr(aC, aN)
{
@ -824,9 +791,8 @@ function genTreeText(aT)
// Indent more if this entry is narrower than its parent, and update
// aIndentGuide accordingly.
var tMemoryUsedStr = formatBytes(aT._amount);
var tBytesLength = tMemoryUsedStr.length;
var extraIndentLength = Math.max(aParentBytesLength - tBytesLength, 0);
var tString = aT.toString();
var extraIndentLength = Math.max(aParentStringLength - tString.length, 0);
if (extraIndentLength > 0) {
for (var i = 0; i < extraIndentLength; i++) {
indent += kHorizontal;
@ -845,20 +811,20 @@ function genTreeText(aT)
}
perc = "<span class='mrPerc'>(" + perc + "%)</span> ";
var text = indent + genMrValueText(tMemoryUsedStr) + " " + perc +
var text = indent + genMrValueText(tString) + " " + perc +
genMrNameText(aT._kind, aT._description, aT._name,
aT._hasProblem, aT._nMerged);
for (var i = 0; i < aT._kids.length; i++) {
// 3 is the standard depth, the callee adjusts it if necessary.
aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
text += genTreeText2(aT._kids[i], aIndentGuide, tBytesLength);
text += genTreeText2(aT._kids[i], aIndentGuide, tString.length);
aIndentGuide.pop();
}
return text;
}
var text = genTreeText2(aT, [], treeBytesLength);
var text = genTreeText2(aT, [], rootStringLength);
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.
const desc =
"This tree covers explicit memory allocations by the application, " +
@ -877,55 +843,79 @@ function genTreeText(aT)
"'>Explicit Allocations</h2>\n" + "<pre>" + text + "</pre>\n";
}
function OtherReporter(aPath, aUnits, aAmount, aDescription,
aNMerged)
{
// Nb: _kind is not needed, it's always KIND_OTHER.
this._path = aPath;
this._units = aUnits;
if (aAmount === kUnknown) {
this._amount = 0;
this._hasProblem = true;
} else {
this._amount = aAmount;
}
this._description = aDescription;
this.asString = this.toString();
}
OtherReporter.prototype = {
toString: function() {
switch(this._units) {
case UNITS_BYTES: return formatBytes(this._amount);
case UNITS_COUNT:
case UNITS_COUNT_CUMULATIVE: return formatInt(this._amount);
case UNITS_PERCENTAGE: return formatPercentage(this._amount);
default:
assert(false, "bad units in OtherReporter.toString");
}
}
};
OtherReporter.compare = function(a, b) {
return a._path < b._path ? -1 :
a._path > b._path ? 1 :
0;
};
/**
* Generates the text for the "Other Measurements" section.
*
* @param aReporters
* Table of reporters for this process, indexed by _path
* @return The generated text
* @param aReportersByProcess
* Table of Reporters for this process, indexed by _path.
* @return The generated text.
*/
function genOtherText(aReporters)
function genOtherText(aReportersByProcess)
{
// Generate an array of Reporter-like elements, stripping out all the
// reporters that have already been handled. Also find the width of the
// Reporters that have already been handled. Also find the width of the
// widest element, so we can format things nicely.
var maxAmountLength = 0;
var rArray = [];
for (var path in aReporters) {
var r = aReporters[path];
var maxStringLength = 0;
var otherReporters = [];
for (var path in aReportersByProcess) {
var r = aReportersByProcess[path];
if (!r._done) {
assert(r._kind === KIND_OTHER, "_kind !== KIND_OTHER for " + r._path);
assert(r.nMerged === undefined); // we don't allow dup'd OTHER reporters
var hasProblem = false;
if (r._amount === kUnknown) {
hasProblem = true;
}
var elem = {
_path: r._path,
_kind: r._kind,
_units: r._units,
_amount: hasProblem ? 0 : r._amount,
_description: r._description,
_hasProblem: hasProblem,
_nMerged: r._nMerged
};
rArray.push(elem);
var thisAmountLength = formatReporterAmount(elem).length;
if (thisAmountLength > maxAmountLength) {
maxAmountLength = thisAmountLength;
var o = new OtherReporter(r._path, r._units, r._amount, r._description);
otherReporters.push(o);
if (o.asString.length > maxStringLength) {
maxStringLength = o.asString.length;
}
}
}
rArray.sort(cmpOtherReporters);
otherReporters.sort(OtherReporter.compare);
// Generate text for the not-yet-printed values.
var text = "";
for (var i = 0; i < rArray.length; i++) {
var elem = rArray[i];
assert(elem._kind === KIND_OTHER,
"elem._kind is not KIND_OTHER for " + elem._path);
text += genMrValueText(
pad(formatReporterAmount(elem), maxAmountLength, ' ')) + " ";
text += genMrNameText(elem._kind, elem._description, elem._path,
elem._hasProblem, elem._nMerged);
for (var i = 0; i < otherReporters.length; i++) {
var o = otherReporters[i];
text += genMrValueText(pad(o.asString, maxStringLength, ' ')) + " ";
text += genMrNameText(KIND_OTHER, o._description, o._path, o._hasProblem);
}
// Nb: the newlines give nice spacing if we cut+paste into a text buffer.

View File

@ -85,8 +85,7 @@
f("", "explicit/g/a", HEAP, 6 * MB),
f("", "explicit/g/b", HEAP, 5 * MB),
f("", "other1", OTHER, 111 * MB),
f2("", "other4", OTHER, COUNT_CUMULATIVE, 888),
f2("", "unknown-unit", OTHER, /*bogus unit*/999, 999)
f2("", "other4", OTHER, COUNT_CUMULATIVE, 888)
];
var fakeMultiReporters = [
{ collectReports: function(cbObj, closure) {
@ -203,7 +202,6 @@ Other Measurements\n\
888 -- other4\n\
45.67% -- perc1\n\
100.00% -- perc2\n\
(???) -- unknown-unit\n\
\n\
2nd Process\n\
\n\
@ -276,7 +274,6 @@ Other Measurements\n\
888 -- other4\n\
45.67% -- perc1\n\
100.00% -- perc2\n\
(???) -- unknown-unit\n\
\n\
2nd Process\n\
\n\

View File

@ -120,7 +120,7 @@ sum_many_##name##_##suffix( \
#include "typedefs.h"
#undef ABI
#if defined(_WIN32) && !defined(_WIN64)
#if defined(_WIN32)
void NS_STDCALL
test_void_t_stdcall()
@ -134,7 +134,7 @@ test_void_t_stdcall()
#include "typedefs.h"
#undef ABI
#endif /* defined(_WIN32) && !defined(_WIN64) */
#endif /* defined(_WIN32) */
#define DEFINE_TYPE(name, type, ffiType) \
struct align_##name { \
@ -321,13 +321,13 @@ test_closure_cdecl(PRInt8 i, test_func_ptr f)
return f(i);
}
#if defined(_WIN32) && !defined(_WIN64)
#if defined(_WIN32)
PRInt32
test_closure_stdcall(PRInt8 i, test_func_ptr_stdcall f)
{
return f(i);
}
#endif /* defined(_WIN32) && !defined(_WIN64) */
#endif /* defined(_WIN32) */
template <typename T> struct PromotedTraits {
typedef T type;

View File

@ -69,7 +69,7 @@ NS_EXTERN_C
#include "typedefs.h"
#if defined(_WIN32) && !defined(_WIN64)
#if defined(_WIN32)
EXPORT_STDCALL(void) test_void_t_stdcall();
EXPORT_STDCALL(void*) get_voidptr_t_stdcall();
@ -89,7 +89,7 @@ NS_EXTERN_C
#include "typedefs.h"
#endif /* defined(_WIN32) && !defined(_WIN64) */
#endif /* defined(_WIN32) */
NS_EXPORT PRInt32 test_ansi_len(const char*);
NS_EXPORT PRInt32 test_wide_len(const PRUnichar*);
@ -190,10 +190,10 @@ NS_EXTERN_C
typedef PRInt32 (* test_func_ptr)(PRInt8);
NS_EXPORT PRInt32 test_closure_cdecl(PRInt8, test_func_ptr);
#if defined(_WIN32) && !defined(_WIN64)
#if defined(_WIN32)
typedef PRInt32 (NS_STDCALL * test_func_ptr_stdcall)(PRInt8);
NS_EXPORT PRInt32 test_closure_stdcall(PRInt8, test_func_ptr_stdcall);
#endif /* defined(_WIN32) && !defined(_WIN64) */
#endif /* defined(_WIN32) */
NS_EXPORT PRInt32 test_callme(PRInt8);
NS_EXPORT void* test_getfn();

View File

@ -681,7 +681,6 @@ function run_basic_abi_tests(library, t, name, toprimitive,
toprimitive, get_test, set_tests, sum_tests, sum_many_tests);
#ifdef WIN32
#ifndef HAVE_64BIT_OS
function declare_fn_stdcall(fn_t, prefix) {
return library.declare(prefix + name + "_stdcall", fn_t);
}
@ -696,7 +695,6 @@ function run_basic_abi_tests(library, t, name, toprimitive,
ctypes.char.ptr);
let hello = ctypes.char.array()("hello!");
do_check_eq(charupper(hello).readString(), "HELLO!");
#endif
#endif
// Check the alignment of the type, and its behavior in a struct,
@ -1877,14 +1875,12 @@ function run_FunctionType_tests() {
do_check_eq(f3_t.name, "char*(*(**[][8])())[]");
#ifdef WIN32
#ifndef HAVE_64BIT_OS
f3_t = ctypes.FunctionType(ctypes.stdcall_abi,
ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
do_check_eq(f3_t.name, "char*(*(__stdcall **[][8])())[]");
f3_t = ctypes.FunctionType(ctypes.winapi_abi,
ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
do_check_eq(f3_t.name, "char*(*(WINAPI **[][8])())[]");
#endif
#endif
let f4_t = ctypes.FunctionType(ctypes.default_abi,
@ -2090,11 +2086,12 @@ function run_void_tests(library) {
}, Error);
#ifdef WIN32
#ifndef HAVE_64BIT_OS
test_void_t = library.declare("test_void_t_stdcall", ctypes.stdcall_abi, ctypes.void_t);
do_check_eq(test_void_t(), undefined);
// Check that WINAPI symbol lookup for a regular stdcall function fails.
// Check that WINAPI symbol lookup for a regular stdcall function fails on
// Win32 (it's all the same on Win64 though).
#ifndef HAVE_64BIT_OS
do_check_throws(function() {
let test_winapi_t = library.declare("test_void_t_stdcall", ctypes.winapi_abi, ctypes.void_t);
}, Error);
@ -2253,7 +2250,6 @@ function run_closure_tests(library)
{
run_single_closure_tests(library, ctypes.default_abi, "cdecl");
#ifdef WIN32
#ifndef HAVE_64BIT_OS
run_single_closure_tests(library, ctypes.stdcall_abi, "stdcall");
// Check that attempting to construct a ctypes.winapi_abi closure throws.
@ -2264,7 +2260,6 @@ function run_closure_tests(library)
let fn_t = ctypes.FunctionType(ctypes.winapi_abi, ctypes.int32_t, []).ptr;
do_check_throws(function() { fn_t(closure_fn) }, Error);
#endif
#endif
}
function run_single_closure_tests(library, abi, suffix)
@ -2324,7 +2319,6 @@ function run_variadic_tests(library) {
}, Error);
#ifdef WIN32
#ifndef HAVE_64BIT_OS
do_check_throws(function() {
ctypes.FunctionType(ctypes.stdcall_abi, ctypes.bool,
[ctypes.bool, "..."]);
@ -2333,7 +2327,6 @@ function run_variadic_tests(library) {
ctypes.FunctionType(ctypes.winapi_abi, ctypes.bool,
[ctypes.bool, "..."]);
}, Error);
#endif
#endif
do_check_throws(function() {

View File

@ -50,6 +50,8 @@ HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of J
HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector")
HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)")
HISTOGRAM(TELEMETRY_SUCCESS, 0, 1, 2, BOOLEAN, "Successful telemetry submission")
HISTOGRAM(MEMORY_JS_COMPARTMENTS_SYSTEM, 1, 1000, 10, EXPONENTIAL, "Total JavaScript compartments used for add-ons and internals.")
HISTOGRAM(MEMORY_JS_COMPARTMENTS_USER, 1, 1000, 10, EXPONENTIAL, "Total JavaScript compartments used for web pages")
HISTOGRAM(MEMORY_JS_GC_HEAP, 1024, 512 * 1024, 10, EXPONENTIAL, "Memory used by the garbage-collected JavaScript heap (KB)")
HISTOGRAM(MEMORY_RESIDENT, 32 * 1024, 1024 * 1024, 10, EXPONENTIAL, "Resident memory size (KB)")
HISTOGRAM(MEMORY_LAYOUT_ALL, 1024, 64 * 1024, 10, EXPONENTIAL, "Memory used by layout (KB)")

View File

@ -55,6 +55,8 @@ const TELEMETRY_DELAY = 60000;
// about:memory values to turn into histograms
const MEM_HISTOGRAMS = {
"js-gc-heap": "MEMORY_JS_GC_HEAP",
"js-compartments-system": "MEMORY_JS_COMPARTMENTS_SYSTEM",
"js-compartments-user": "MEMORY_JS_COMPARTMENTS_USER",
"resident": "MEMORY_RESIDENT",
"explicit/layout/all": "MEMORY_LAYOUT_ALL",
"explicit/images/content/used/uncompressed":

View File

@ -27,6 +27,10 @@
<label id="panellabel" value="This is some text." height="65"/>
</panel>
<panel id="bigpanel" type="arrow" onpopupshown="checkBigPanel(this)" onpopuphidden="SimpleTest.finish()">
<button label="This is some text." height="3000"/>
</panel>
<script type="application/javascript">
<![CDATA[
@ -136,6 +140,9 @@ function nextTest()
setScale(frames[0], 1);
*/
$("bigpanel").openPopup($("topleft"), "after_start", 0, 0, false, false, null);
yield;
SimpleTest.finish();
yield;
}
@ -220,6 +227,12 @@ function checkPanelPosition(panel)
panel.hidePopup();
}
function checkBigPanel(panel)
{
ok(panel.firstChild.getBoundingClientRect().height < 2800, "big panel height");
panel.hidePopup();
}
SimpleTest.waitForFocus(startTest);
]]>

View File

@ -302,12 +302,12 @@
<binding id="arrowpanel" extends="chrome://global/content/bindings/popup.xml#panel">
<content flip="both" side="top" position="bottomcenter topleft">
<xul:box anonid="container" class="panel-arrowcontainer">
<xul:box anonid="container" class="panel-arrowcontainer" flex="1">
<xul:box anonid="arrowbox" class="panel-arrowbox">
<xul:image anonid="arrow" class="panel-arrow"/>
</xul:box>
<xul:box class="panel-arrowcontent">
<xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack">
<xul:box class="panel-arrowcontent" flex="1">
<xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack" flex="1">
<children/>
<xul:box class="panel-inner-arrowcontentfooter" xbl:inherits="footertype" hidden="true"/>
</xul:box>

View File

@ -294,16 +294,16 @@ public:
static void MouseMoved(NSEvent* aEvent);
static void OnDestroyView(ChildView* aView);
static void OnDestroyWindow(NSWindow* aWindow);
static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
ChildView* aView, BOOL isClickThrough = NO);
static void MouseExitedWindow(NSEvent* aEvent);
static void MouseEnteredWindow(NSEvent* aEvent);
static void ReEvaluateMouseEnterState(NSEvent* aEvent = nil);
static ChildView* ViewForEvent(NSEvent* aEvent);
static ChildView* sLastMouseEventView;
private:
static NSWindow* WindowForEvent(NSEvent* aEvent);
static NSWindow* sWindowUnderMouse;
};
//-------------------------------------------------------------------------

View File

@ -112,14 +112,6 @@ extern "C" {
CG_EXTERN void CGContextResetCTM(CGContextRef);
CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
CG_EXTERN void CGContextResetClip(CGContextRef);
// CGSPrivate.h
typedef NSInteger CGSConnection;
typedef NSInteger CGSWindow;
extern CGSConnection _CGSDefaultConnection();
extern CGError CGSGetScreenRectForWindow(const CGSConnection cid, CGSWindow wid, CGRect *outRect);
extern CGError CGSGetWindowLevel(const CGSConnection cid, CGSWindow wid, CGWindowLevel *level);
extern CGError CGSGetWindowAlpha(const CGSConnection cid, const CGSWindow wid, float* alpha);
}
// defined in nsMenuBarX.mm
@ -133,6 +125,7 @@ PRBool gChildViewMethodsSwizzled = PR_FALSE;
extern nsISupportsArray *gDraggedTransferables;
ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
#ifdef INVALIDATE_DEBUGGING
static void blinkRect(Rect* r);
@ -1236,6 +1229,24 @@ nsresult nsChildView::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
if (!event)
return NS_ERROR_FAILURE;
if ([[mView window] isKindOfClass:[BaseWindow class]]) {
// Tracking area events don't end up in their tracking areas when sent
// through [NSApp sendEvent:], so pass them directly to the right methods.
BaseWindow* window = (BaseWindow*)[mView window];
if (aNativeMessage == NSMouseEntered) {
[window mouseEntered:event];
return NS_OK;
}
if (aNativeMessage == NSMouseExited) {
[window mouseExited:event];
return NS_OK;
}
if (aNativeMessage == NSMouseMoved) {
[window mouseMoved:event];
return NS_OK;
}
}
[NSApp sendEvent:event];
return NS_OK;
@ -3234,11 +3245,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
mGeckoChild->DispatchEvent(&event, status);
}
- (void)mouseMoved:(NSEvent*)aEvent
{
ChildViewMouseTracker::MouseMoved(aEvent);
}
- (void)handleMouseMoved:(NSEvent*)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@ -4756,8 +4762,33 @@ NSEvent* gLastDragMouseDownEvent = nil;
void
ChildViewMouseTracker::OnDestroyView(ChildView* aView)
{
if (sLastMouseEventView == aView)
if (sLastMouseEventView == aView) {
sLastMouseEventView = nil;
}
}
void
ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow)
{
if (sWindowUnderMouse == aWindow) {
sWindowUnderMouse = nil;
}
}
void
ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent)
{
sWindowUnderMouse = [aEvent window];
ReEvaluateMouseEnterState(aEvent);
}
void
ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent)
{
if (sWindowUnderMouse == [aEvent window]) {
sWindowUnderMouse = nil;
ReEvaluateMouseEnterState(aEvent);
}
}
void
@ -4781,20 +4812,19 @@ ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent)
void
ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
{
ReEvaluateMouseEnterState(aEvent);
MouseEnteredWindow(aEvent);
[sLastMouseEventView handleMouseMoved:aEvent];
}
ChildView*
ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
{
NSWindow* window = WindowForEvent(aEvent);
NSWindow* window = sWindowUnderMouse;
if (!window)
return nil;
NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
NS_ASSERTION(view, "How can the mouse be over a window but not over a view in that window?");
if (![view isKindOfClass:[ChildView class]])
return nil;
@ -4805,102 +4835,6 @@ ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
}
static CGWindowLevel kDockWindowLevel = 0;
static CGWindowLevel kPopupWindowLevel = 0;
static BOOL WindowNumberIsUnderPoint(NSInteger aWindowNumber, NSPoint aPoint) {
NSWindow* window = [NSApp windowWithWindowNumber:aWindowNumber];
if (window) {
// This is one of our own windows.
return NSMouseInRect(aPoint, [window frame], NO);
}
CGSConnection cid = _CGSDefaultConnection();
if (!kDockWindowLevel) {
// These constants are in fact function calls, so cache them.
kDockWindowLevel = kCGDockWindowLevel;
kPopupWindowLevel = kCGPopUpMenuWindowLevel;
}
// Some things put transparent windows on top of everything. Ignore them.
CGWindowLevel level;
if ((kCGErrorSuccess == CGSGetWindowLevel(cid, aWindowNumber, &level)) &&
(level == kDockWindowLevel || // Transparent layer, spanning the whole screen
level > kPopupWindowLevel)) // Snapz Pro X while recording a screencast
return false;
// Ignore transparent windows.
float alpha;
if ((kCGErrorSuccess == CGSGetWindowAlpha(cid, aWindowNumber, &alpha)) &&
alpha < 0.1f)
return false;
CGRect rect;
if (kCGErrorSuccess != CGSGetScreenRectForWindow(cid, aWindowNumber, &rect))
return false;
CGPoint point = { aPoint.x, nsCocoaUtils::FlippedScreenY(aPoint.y) };
return CGRectContainsPoint(rect, point);
}
// Find the window number of the window under the given point, regardless of
// which app the window belongs to. Returns 0 if no window was found.
static NSInteger WindowNumberAtPoint(NSPoint aPoint) {
// We'd like to use the new windowNumberAtPoint API on 10.6 but we can't rely
// on it being up-to-date. For example, if we've just opened a window,
// windowNumberAtPoint might not know about it yet, so we'd send events to the
// wrong window. See bug 557986.
// So we'll have to find the right window manually by iterating over all
// windows on the screen and testing whether the mouse is inside the window's
// rect. We do this using private CGS functions.
// Another way of doing it would be to use tracking rects, but those are
// view-controlled, so they need to be reset whenever an NSView changes its
// size or position, which is expensive. See bug 300904 comment 20.
// A problem with using the CGS functions is that we only look at the windows'
// rects, not at the transparency of the actual pixel the mouse is over. This
// means that we won't treat transparent pixels as transparent to mouse
// events, which is a disadvantage over using tracking rects and leads to the
// crummy window level workarounds in WindowNumberIsUnderPoint.
// But speed is more important.
// Get the window list.
NSInteger windowCount;
NSCountWindows(&windowCount);
NSInteger* windowList = (NSInteger*)malloc(sizeof(NSInteger) * windowCount);
if (!windowList)
return nil;
// The list we get back here is in order from front to back.
NSWindowList(windowCount, windowList);
for (NSInteger i = 0; i < windowCount; i++) {
NSInteger windowNumber = windowList[i];
if (WindowNumberIsUnderPoint(windowNumber, aPoint)) {
free(windowList);
return windowNumber;
}
}
free(windowList);
return 0;
}
// Find Gecko window under the mouse. Returns nil if the mouse isn't over
// any of our windows.
NSWindow*
ChildViewMouseTracker::WindowForEvent(NSEvent* anEvent)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
NSPoint screenPoint = nsCocoaUtils::ScreenLocationForEvent(anEvent);
NSInteger windowNumber = WindowNumberAtPoint(screenPoint);
// This will return nil if windowNumber belongs to a window that we don't own.
return [NSApp windowWithWindowNumber:windowNumber];
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
BOOL
ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
ChildView* aView, BOOL aIsClickThrough)

View File

@ -81,6 +81,8 @@ typedef struct _nsCocoaWindowList {
// is ridiculously slow, so we cache it in the toplevel window for all
// descendants to use.
float mDPI;
NSTrackingArea* mTrackingArea;
}
- (void)importState:(NSDictionary*)aState;
@ -94,6 +96,12 @@ typedef struct _nsCocoaWindowList {
- (void)invalidateShadow;
- (float)getDPI;
- (void)mouseEntered:(NSEvent*)aEvent;
- (void)mouseExited:(NSEvent*)aEvent;
- (void)mouseMoved:(NSEvent*)aEvent;
- (void)updateTrackingArea;
- (NSView*)trackingAreaView;
@end
@interface NSWindow (Undocumented)

View File

@ -642,7 +642,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
parentIsSheet) {
piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
[NSApp endSheet:nativeParentWindow];
[nativeParentWindow setAcceptsMouseMovedEvents:NO];
}
nsCocoaWindow* sheetShown = nsnull;
@ -657,7 +656,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
// Only set contextInfo if our parent isn't a sheet.
NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
[TopLevelWindowData deactivateInWindow:mSheetWindowParent];
[mWindow setAcceptsMouseMovedEvents:YES];
[NSApp beginSheet:mWindow
modalForWindow:mSheetWindowParent
modalDelegate:mDelegate
@ -684,7 +682,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
NSInteger windowNumber = [mWindow windowNumber];
[mWindow _setWindowNumber:-1];
[mWindow _setWindowNumber:windowNumber];
[mWindow setAcceptsMouseMovedEvents:YES];
// For reasons that aren't yet clear, calls to [NSWindow orderFront:] or
// [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000)
// creating CGSWindow", which in turn triggers an internal inconsistency
@ -716,7 +713,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
ordered:NSWindowAbove];
}
else {
[mWindow setAcceptsMouseMovedEvents:YES];
NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
[mWindow makeKeyAndOrderFront:nil];
NS_OBJC_END_TRY_LOGONLY_BLOCK;
@ -743,8 +739,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
// hide the sheet
[NSApp endSheet:mWindow];
[mWindow setAcceptsMouseMovedEvents:NO];
[TopLevelWindowData deactivateInWindow:mWindow];
nsCocoaWindow* siblingSheetToShow = nsnull;
@ -774,7 +768,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
// If there are no sibling sheets, but the parent is a sheet, restore
// it. It wasn't sent any deactivate events when it was hidden, so
// don't call through Show, just let the OS put it back up.
[nativeParentWindow setAcceptsMouseMovedEvents:YES];
[NSApp beginSheet:nativeParentWindow
modalForWindow:sheetParent
modalDelegate:[nativeParentWindow delegate]
@ -787,7 +780,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
[sheetParent makeKeyAndOrderFront:nil];
NS_OBJC_END_TRY_LOGONLY_BLOCK;
[sheetParent setAcceptsMouseMovedEvents:YES];
}
SendSetZLevelEvent();
}
@ -813,10 +805,6 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
if (mWindowType == eWindowType_popup)
[NSApp _removeWindowFromCache:mWindow];
// it's very important to turn off mouse moved events when hiding a window, otherwise
// the windows' tracking rects will interfere with each other. (bug 356528)
[mWindow setAcceptsMouseMovedEvents:NO];
// If our popup window is a non-native context menu, tell the OS (and
// other programs) that a menu has closed.
if ([mWindow isKindOfClass:[PopupWindow class]] &&
@ -1414,7 +1402,6 @@ NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
mPopupContentView->SetFocus(aState);
}
else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) {
[mWindow setAcceptsMouseMovedEvents:YES];
[mWindow makeKeyAndOrderFront:nil];
SendSetZLevelEvent();
}
@ -1763,6 +1750,9 @@ PRBool nsCocoaWindow::ShouldFocusPlugin()
- (void)windowDidResize:(NSNotification *)aNotification
{
BaseWindow* window = [aNotification object];
[window updateTrackingArea];
if (!mGeckoWindow)
return;
@ -1970,6 +1960,11 @@ GetDPI(NSWindow* aWindow)
return (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
}
@interface BaseWindow(Private)
- (void)removeTrackingArea;
- (void)cursorUpdated:(NSEvent*)aEvent;
@end
@implementation BaseWindow
- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
@ -1981,6 +1976,8 @@ GetDPI(NSWindow* aWindow)
mInactiveTitlebarColor = nil;
mScheduledShadowInvalidation = NO;
mDPI = GetDPI(self);
mTrackingArea = nil;
[self updateTrackingArea];
return self;
}
@ -1989,6 +1986,8 @@ GetDPI(NSWindow* aWindow)
{
[mActiveTitlebarColor release];
[mInactiveTitlebarColor release];
[self removeTrackingArea];
ChildViewMouseTracker::OnDestroyWindow(self);
[super dealloc];
}
@ -2074,6 +2073,55 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
return mDPI;
}
- (NSView*)trackingAreaView
{
NSView* contentView = [self contentView];
return [contentView superview] ? [contentView superview] : contentView;
}
- (void)removeTrackingArea
{
if (mTrackingArea) {
[[self trackingAreaView] removeTrackingArea:mTrackingArea];
[mTrackingArea release];
mTrackingArea = nil;
}
}
- (void)updateTrackingArea
{
[self removeTrackingArea];
NSView* view = [self trackingAreaView];
const NSTrackingAreaOptions options =
NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways;
mTrackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds]
options:options
owner:self
userInfo:nil];
[view addTrackingArea:mTrackingArea];
}
- (void)mouseEntered:(NSEvent*)aEvent
{
ChildViewMouseTracker::MouseEnteredWindow(aEvent);
}
- (void)mouseExited:(NSEvent*)aEvent
{
ChildViewMouseTracker::MouseExitedWindow(aEvent);
}
- (void)mouseMoved:(NSEvent*)aEvent
{
ChildViewMouseTracker::MouseMoved(aEvent);
}
- (void)cursorUpdated:(NSEvent*)aEvent
{
// Nothing to do here, but NSTrackingArea wants us to implement this method.
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
// Claim the window doesn't respond to this so that the system
@ -2456,87 +2504,6 @@ TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
@implementation PopupWindow
// The OS treats our custom popup windows very strangely -- many mouse events
// sent to them never reach their target NSView objects. (That these windows
// are borderless and of level NSPopUpMenuWindowLevel may have something to do
// with it.) The best solution is to pre-empt the OS, as follows. (All
// events for a given NSWindow object go through its sendEvent: method.)
- (void)sendEvent:(NSEvent *)anEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NSView *target = nil;
NSView *contentView = nil;
NSEventType type = [anEvent type];
NSPoint windowLocation = NSZeroPoint;
switch (type) {
case NSScrollWheel:
case NSLeftMouseDown:
case NSLeftMouseUp:
case NSRightMouseDown:
case NSRightMouseUp:
case NSOtherMouseDown:
case NSOtherMouseUp:
case NSMouseMoved:
case NSLeftMouseDragged:
case NSRightMouseDragged:
case NSOtherMouseDragged:
if ((contentView = [self contentView])) {
// Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
// If the hit test failed, the event is targeted here but is not over the window.
// Send it to our content view.
if (!target)
target = contentView;
}
break;
default:
break;
}
if (target) {
switch (type) {
case NSScrollWheel:
[target scrollWheel:anEvent];
break;
case NSLeftMouseUp:
[target mouseUp:anEvent];
break;
case NSRightMouseDown:
[target rightMouseDown:anEvent];
break;
case NSRightMouseUp:
[target rightMouseUp:anEvent];
break;
case NSOtherMouseDown:
[target otherMouseDown:anEvent];
break;
case NSOtherMouseUp:
[target otherMouseUp:anEvent];
break;
case NSMouseMoved:
[target mouseMoved:anEvent];
break;
case NSLeftMouseDragged:
[target mouseDragged:anEvent];
break;
case NSRightMouseDragged:
[target rightMouseDragged:anEvent];
break;
case NSOtherMouseDragged:
[target otherMouseDragged:anEvent];
break;
default:
[super sendEvent:anEvent];
break;
}
} else {
[super sendEvent:anEvent];
}
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
{

View File

@ -83,7 +83,6 @@ protected:
CFRunLoopSourceRef mSleepWakeNotificationRLS;
io_object_t mPowerNotifier;
EventHandlerRef mEventMonitorHandler;
CFMachPortRef mEventTapPort;
CFRunLoopSourceRef mEventTapRLS;
};

View File

@ -87,7 +87,6 @@ static PRUintn gToolkitTLSIndex = 0;
nsToolkit::nsToolkit()
: mInited(false)
, mSleepWakeNotificationRLS(nsnull)
, mEventMonitorHandler(nsnull)
, mEventTapPort(nsnull)
, mEventTapRLS(nsnull)
{
@ -202,18 +201,6 @@ nsToolkit::RemoveSleepWakeNotifcations()
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// This is the callback used in RegisterForAllProcessMouseEvents.
static OSStatus EventMonitorHandler(EventHandlerCallRef aCaller, EventRef aEvent, void* aRefcon)
{
// Up to Mac OS 10.4 (or when building with the 10.4 SDK), installing a Carbon
// event handler like this one caused the OS to post the equivalent Cocoa
// events to [NSApp sendEvent:]. When using the 10.5 SDK, this doesn't happen
// any more, so we need to do it manually.
[NSApp sendEvent:[NSEvent eventWithEventRef:aEvent]];
return eventNotHandledErr;
}
// Converts aPoint from the CoreGraphics "global display coordinate" system
// (which includes all displays/screens and has a top-left origin) to its
// (presumed) Cocoa counterpart (assumed to be the same as the "screen
@ -275,12 +262,6 @@ nsToolkit::RegisterForAllProcessMouseEvents()
return;
#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
if (!mEventMonitorHandler) {
EventTypeSpec kEvents[] = {{kEventClassMouse, kEventMouseMoved}};
InstallEventHandler(GetEventMonitorTarget(), EventMonitorHandler,
GetEventTypeCount(kEvents), kEvents, 0,
&mEventMonitorHandler);
}
if (!mEventTapRLS) {
// Using an event tap for mouseDown events (instead of installing a
// handler for them on the EventMonitor target) works around an Apple
@ -320,10 +301,6 @@ nsToolkit::UnregisterAllProcessMouseEventHandlers()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (mEventMonitorHandler) {
RemoveEventHandler(mEventMonitorHandler);
mEventMonitorHandler = nsnull;
}
if (mEventTapRLS) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mEventTapRLS,
kCFRunLoopDefaultMode);

View File

@ -72,12 +72,14 @@
}
function onTestsFinished() {
clearTimeout(gAfterLoopExecution);
observe(window, eventMonitor, false);
observe(gRightWindow, eventMonitor, false);
observe(gPopup, eventMonitor, false);
gRightWindow.close();
var openerSimpleTest = window.opener.wrappedJSObject.SimpleTest;
window.close();
window.opener.wrappedJSObject.SimpleTest.finish();
openerSimpleTest.finish();
}
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -128,6 +130,8 @@
var gExpectedEvents = [];
var gRightWindow = null, gPopup = null;
var gCurrentMouseX = 0, gCurrentMouseY = 0;
var gAfterLoopExecution = 0;
function testMouse(x, y, msg, elem, win, exp, flags, callback) {
clearExpectedEvents();
@ -137,14 +141,16 @@
gExpectedEvents.push(expEv);
});
printDebug("sending event: " + x + ", " + y + " (" + msg + ")\n");
gCurrentMouseX = x;
gCurrentMouseY = y;
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendNativeMouseEvent(x, y, msg, flags || 0, elem);
SimpleTest.executeSoon(function () {
gAfterLoopExecution = setTimeout(function () {
clearExpectedEvents();
callback();
});
}, 0);
}
function eventListenOnce(elem, name, callback) {
@ -182,10 +188,15 @@
}
function processEvent(e) {
if (e.screenX != gCurrentMouseX || e.screenY != gCurrentMouseY) {
todo(false, "Oh no! Received a stray event from a confused tracking area. Aborting test.");
onTestsFinished();
return;
}
var expectedEvent = gExpectedEvents.shift();
if (!expectedEvent) {
ok(false, "received event I didn't expect: " + eventToString(e));
return true;
return;
}
if (e.type != expectedEvent.type) {
// Didn't get expectedEvent.
@ -306,6 +317,10 @@
{ type: "mouseup", target: rightElem },
{ type: "click", target: rightElem },
]],
// Move the mouse back over the left window, which is inactive.
[150, 170, NSMouseMoved, null, left, [
{ type: "mouseout", target: rightElem },
]],
// Now we're being sneaky. The left window is inactive, but *right*-clicks to it
// should still get through. Test that.
// Ideally we'd be bracketing that event with over and out events, too, but it
@ -327,7 +342,6 @@
// Still, mouseout and mouseover events should fire.
function raiseLeftWindow(callback) {
clearExpectedEvents();
gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
},
@ -391,25 +405,20 @@
// Install the tooltip, but don't show it yet.
function setTooltip(callback) {
rightElem.setAttribute("tooltip", "tip");
callback();
},
// Move the mouse to trigger the appearance of the tooltip.
[410, 180, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
// Wait for the tooltip to appear.
function (callback) {
gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
eventListenOnce(rightElem, "popupshown", callback);
gCurrentMouseX = 410;
gCurrentMouseY = 180;
var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
},
// Now the tooltip is visible.
// Move the mouse a little to the right, but send the event to the tooltip's
// widget, even though the mouse is not over the tooltip, because that's what
// Mac OS X does.
[411, 180, NSMouseMoved, tooltip, right, [
// Move the mouse a little to the right.
[411, 180, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
// Move another pixel. This time send the event to the right widget.
// However, that must not make a difference.
// Move another pixel.
[412, 180, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
@ -434,22 +443,21 @@
// Now we move the mouse over the part where the panel rect intersects the
// right window's rect. Since the panel is under the window, all the events
// should target the right window.
// Try with sending to three different targets.
[390, 170, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
[390, 171, NSMouseMoved, null, left, [
[390, 171, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
[391, 171, NSMouseMoved, panel, left, [
[391, 171, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
// Now move off the right window, so that the mouse is directly over the
// panel.
[260, 170, NSMouseMoved, null, left, [
[260, 170, NSMouseMoved, panel, left, [
{ type: "mouseout", target: rightElem },
]],
[260, 171, NSMouseMoved, null, left, [
[260, 171, NSMouseMoved, panel, left, [
]],
[261, 171, NSMouseMoved, panel, left, [
]],
@ -464,11 +472,11 @@
},
// Now mouse events should get through to the panel (which is now over the
// right window).
[387, 170, NSMouseMoved, null, right, [
[387, 170, NSMouseMoved, panel, left, [
{ type: "mouseover", target: panel },
{ type: "mousemove", target: panel },
]],
[387, 171, NSMouseMoved, null, left, [
[387, 171, NSMouseMoved, panel, left, [
{ type: "mousemove", target: panel },
]],
[388, 171, NSMouseMoved, panel, left, [
@ -486,12 +494,12 @@
// Last test for this part: Hit testing in the Canyon of Nowhere -
// the pixel row directly south of the panel, over the left window.
// Before bug 515003 we wrongly thought the mouse wasn't over any window.
[173, 200, NSMouseMoved, panel, left, [
[173, 200, NSMouseMoved, null, left, [
{ type: "mouseout", target: panel },
{ type: "mouseover", target: leftElem },
{ type: "mousemove", target: leftElem },
]],
[173, 201, NSMouseMoved, panel, left, [
[173, 201, NSMouseMoved, null, left, [
{ type: "mousemove", target: leftElem },
]],
@ -567,32 +575,28 @@
{ type: "mouseup", target: rightElem },
{ type: "click", target: rightElem },
]],
// Now we're being sneaky. The left window is inactive, but *right*-clicks to it
// should still get through. Test that.
// Ideally we'd be bracketing that event with over and out events, too, but it
// probably doesn't matter too much.
// Move the mouse back over the left window, which is inactive.
[150, 170, NSMouseMoved, null, left, [
{ type: "mouseout", target: rightElem },
{ type: "mouseover", target: leftElem },
{ type: "mousemove", target: leftElem },
]],
// Right-click it.
[150, 170, NSRightMouseDown, null, left, [
{ type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
{ type: "mousedown", target: leftElem },
{ type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
]],
// Let go of the mouse.
[150, 170, NSRightMouseUp, null, left, [
{ type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
{ type: "mouseup", target: leftElem },
{ type: "click", target: leftElem },
{ type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
]],
// Right clicking hasn't focused it, so the window is still inactive.
// Let's focus it; this time without the mouse, for variaton's sake.
// Still, mouseout and mouseover events should fire.
function raiseLeftWindow(callback) {
clearExpectedEvents();
gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
},
// It's active, so it should respond to mousemove events now.
// It's active and should still respond to mousemove events.
[150, 170, NSMouseMoved, null, left, [
{ type: "mousemove", target: leftElem },
]],
@ -653,27 +657,22 @@
// Time for our next trick: a tooltip!
// Install the tooltip, but don't show it yet.
function setTooltip(callback) {
function setTooltip2(callback) {
rightElem.setAttribute("tooltip", "tip");
callback();
},
// Move the mouse to trigger the appearance of the tooltip.
[410, 180, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
// Wait for the tooltip to appear.
function (callback) {
gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
eventListenOnce(rightElem, "popupshown", callback);
gCurrentMouseX = 410;
gCurrentMouseY = 180;
var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
},
// Now the tooltip is visible.
// Move the mouse a little to the right, but send the event to the tooltip's
// widget, even though the mouse is not over the tooltip, because that's what
// Mac OS X does.
[411, 180, NSMouseMoved, tooltip, right, [
// Move the mouse a little to the right.
[411, 180, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
// Move another pixel. This time send the event to the right widget.
// However, that must not make a difference.
// Move another pixel.
[412, 180, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
@ -698,24 +697,23 @@
// Now we move the mouse over the part where the panel rect intersects the
// right window's rect. Since the panel is under the window, all the events
// should target the right window.
// Try with sending to three different targets.
[390, 170, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
[390, 171, NSMouseMoved, null, left, [
[390, 171, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
[391, 171, NSMouseMoved, panel, left, [
[391, 171, NSMouseMoved, null, right, [
{ type: "mousemove", target: rightElem },
]],
// Now move off the right window, so that the mouse is directly over the
// panel.
[260, 170, NSMouseMoved, null, left, [
[260, 170, NSMouseMoved, panel, left, [
{ type: "mouseout", target: rightElem },
{ type: "mouseover", target: panel },
{ type: "mousemove", target: panel },
]],
[260, 171, NSMouseMoved, null, left, [
[260, 171, NSMouseMoved, panel, left, [
{ type: "mousemove", target: panel },
]],
[261, 171, NSMouseMoved, panel, left, [
@ -733,10 +731,10 @@
function raiseLeftWindowTakeTwo(callback) {
focusAndThen(left, callback);
},
[387, 170, NSMouseMoved, null, right, [
[387, 170, NSMouseMoved, panel, left, [
{ type: "mousemove", target: panel },
]],
[387, 171, NSMouseMoved, null, left, [
[387, 171, NSMouseMoved, panel, left, [
{ type: "mousemove", target: panel },
]],
[388, 171, NSMouseMoved, panel, left, [
@ -754,12 +752,12 @@
// Last test for today: Hit testing in the Canyon of Nowhere -
// the pixel row directly south of the panel, over the left window.
// Before bug 515003 we wrongly thought the mouse wasn't over any window.
[173, 200, NSMouseMoved, panel, left, [
[173, 200, NSMouseMoved, null, left, [
{ type: "mouseout", target: panel },
{ type: "mouseover", target: leftElem },
{ type: "mousemove", target: leftElem },
]],
[173, 201, NSMouseMoved, panel, left, [
[173, 201, NSMouseMoved, null, left, [
{ type: "mousemove", target: leftElem },
]],
];

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Philipp Kewisch <mozilla@kewis.ch>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -430,6 +431,7 @@ ParseManifestCommon(NSLocationType aType, nsILocalFile* aFile,
NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
NS_NAMED_LITERAL_STRING(kApplication, "application");
NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion");
NS_NAMED_LITERAL_STRING(kOs, "os");
NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
NS_NAMED_LITERAL_STRING(kABI, "abi");
@ -439,6 +441,7 @@ ParseManifestCommon(NSLocationType aType, nsILocalFile* aFile,
nsAutoString appID;
nsAutoString appVersion;
nsAutoString geckoVersion;
nsAutoString osTarget;
nsAutoString abi;
@ -453,6 +456,10 @@ ParseManifestCommon(NSLocationType aType, nsILocalFile* aFile,
if (NS_SUCCEEDED(rv))
CopyUTF8toUTF16(s, appVersion);
rv = xapp->GetPlatformVersion(s);
if (NS_SUCCEEDED(rv))
CopyUTF8toUTF16(s, geckoVersion);
nsCOMPtr<nsIXULRuntime> xruntime (do_QueryInterface(xapp));
if (xruntime) {
rv = xruntime->GetOS(s);
@ -576,6 +583,7 @@ ParseManifestCommon(NSLocationType aType, nsILocalFile* aFile,
bool ok = true;
TriState stAppVersion = eUnspecified;
TriState stGeckoVersion = eUnspecified;
TriState stApp = eUnspecified;
TriState stOsVersion = eUnspecified;
TriState stOs = eUnspecified;
@ -591,7 +599,8 @@ ParseManifestCommon(NSLocationType aType, nsILocalFile* aFile,
CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
CheckStringFlag(kABI, wtoken, abi, stABI) ||
CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion))
CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion))
continue;
if (directive->contentflags &&
@ -616,6 +625,7 @@ ParseManifestCommon(NSLocationType aType, nsILocalFile* aFile,
if (!ok ||
stApp == eBad ||
stAppVersion == eBad ||
stGeckoVersion == eBad ||
stOs == eBad ||
stOsVersion == eBad ||
stABI == eBad)

View File

@ -52,6 +52,7 @@
#include "nsIProxyObjectManager.h"
#include "nsIServiceManager.h"
#include "nsIThread.h"
#include "nsIXPConnect.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
@ -208,6 +209,14 @@ nsProxyObjectManager::GetProxyForObject(nsIEventTarget* aTarget,
aTarget = thread.get();
}
if (nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aObj)) {
// Only proxy wrapped JS from the main thread to the main thread
if (!NS_IsMainThread() || aTarget != NS_GetCurrentThread()) {
NS_ABORT_IF_FALSE(false, "GetProxyForObject on wrapped JS not allowed");
return NS_ERROR_FAILURE;
}
}
// check to see if the target is on our thread. If so, just return the
// real object.