зеркало из https://github.com/mozilla/gecko-dev.git
Merging mozilla-inbound into mozilla-central.
This commit is contained in:
Коммит
43d40b8987
|
@ -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);
|
||||
|
|
32
configure.in
32
configure.in
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 <div> 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>
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
295
js/src/jsgc.h
295
js/src/jsgc.h
|
@ -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 */
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */ \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
|
@ -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');
|
|
@ -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');
|
|
@ -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');
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)")
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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);
|
||||
|
||||
]]>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -83,7 +83,6 @@ protected:
|
|||
CFRunLoopSourceRef mSleepWakeNotificationRLS;
|
||||
io_object_t mPowerNotifier;
|
||||
|
||||
EventHandlerRef mEventMonitorHandler;
|
||||
CFMachPortRef mEventTapPort;
|
||||
CFRunLoopSourceRef mEventTapRLS;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
]],
|
||||
];
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче