зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central a=merge
This commit is contained in:
Коммит
19e81b8d28
|
@ -40,6 +40,21 @@ ARIAGridAccessible::NativeRole() const
|
||||||
return r != roles::NOTHING ? r : roles::TABLE;
|
return r != roles::NOTHING ? r : roles::TABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<nsIPersistentProperties>
|
||||||
|
ARIAGridAccessible::NativeAttributes()
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIPersistentProperties> attributes =
|
||||||
|
AccessibleWrap::NativeAttributes();
|
||||||
|
|
||||||
|
if (IsProbablyLayoutTable()) {
|
||||||
|
nsAutoString unused;
|
||||||
|
attributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"),
|
||||||
|
NS_LITERAL_STRING("true"), unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes.forget();
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Table
|
// Table
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
|
|
||||||
// Accessible
|
// Accessible
|
||||||
virtual a11y::role NativeRole() const override;
|
virtual a11y::role NativeRole() const override;
|
||||||
|
virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
|
||||||
virtual TableAccessible* AsTable() override { return this; }
|
virtual TableAccessible* AsTable() override { return this; }
|
||||||
|
|
||||||
// TableAccessible
|
// TableAccessible
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "TableAccessible.h"
|
||||||
|
|
||||||
|
#include "Accessible-inl.h"
|
||||||
|
|
||||||
|
#include "nsTableCellFrame.h"
|
||||||
|
#include "nsTableWrapperFrame.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
using namespace mozilla::a11y;
|
||||||
|
|
||||||
|
bool
|
||||||
|
TableAccessible::IsProbablyLayoutTable()
|
||||||
|
{
|
||||||
|
// Implement a heuristic to determine if table is most likely used for layout.
|
||||||
|
|
||||||
|
// XXX do we want to look for rowspan or colspan, especialy that span all but
|
||||||
|
// a couple cells at the beginning or end of a row/col, and especially when
|
||||||
|
// they occur at the edge of a table?
|
||||||
|
|
||||||
|
// XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
|
||||||
|
// This will allow release trunk builds to be used by testers to refine
|
||||||
|
// the algorithm. Integrate it into Logging.
|
||||||
|
// Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
|
||||||
|
#ifdef SHOW_LAYOUT_HEURISTIC
|
||||||
|
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
|
||||||
|
{ \
|
||||||
|
mLayoutHeuristic = isLayout ? \
|
||||||
|
NS_LITERAL_STRING("layout table: " heuristic) : \
|
||||||
|
NS_LITERAL_STRING("data table: " heuristic); \
|
||||||
|
return isLayout; \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Accessible* thisacc = AsAccessible();
|
||||||
|
|
||||||
|
// Need to see all elements while document is being edited.
|
||||||
|
if (thisacc->Document()->State() & states::EDITABLE) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "In editable document");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if an ARIA role overrides the role from native markup,
|
||||||
|
// but for which we still expose table semantics (treegrid, for example).
|
||||||
|
if (thisacc->HasARIARole()) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "Has role attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
dom::Element* el = thisacc->Elm();
|
||||||
|
if (el->IsMathMLElement(nsGkAtoms::mtable_)) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "MathML matrix");
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(el->IsHTMLElement(nsGkAtoms::table),
|
||||||
|
"Table should not be built by CSS display:table style");
|
||||||
|
|
||||||
|
// Check if datatable attribute has "0" value.
|
||||||
|
if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
|
||||||
|
NS_LITERAL_STRING("0"), eCaseMatters)) {
|
||||||
|
RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for legitimate data table attributes.
|
||||||
|
nsAutoString summary;
|
||||||
|
if (el->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
|
||||||
|
!summary.IsEmpty()) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for legitimate data table elements.
|
||||||
|
Accessible* caption = thisacc->FirstChild();
|
||||||
|
if (caption && caption->IsHTMLCaption() && caption->HasChildren()) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (nsIContent* childElm = el->GetFirstChild(); childElm;
|
||||||
|
childElm = childElm->GetNextSibling()) {
|
||||||
|
if (!childElm->IsHTMLElement())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col,
|
||||||
|
nsGkAtoms::colgroup,
|
||||||
|
nsGkAtoms::tfoot,
|
||||||
|
nsGkAtoms::thead)) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false,
|
||||||
|
"Has col, colgroup, tfoot or thead -- legitimate table structures");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childElm->IsHTMLElement(nsGkAtoms::tbody)) {
|
||||||
|
for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
|
||||||
|
rowElm = rowElm->GetNextSibling()) {
|
||||||
|
if (rowElm->IsHTMLElement(nsGkAtoms::tr)) {
|
||||||
|
for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
|
||||||
|
cellElm = cellElm->GetNextSibling()) {
|
||||||
|
if (cellElm->IsHTMLElement()) {
|
||||||
|
|
||||||
|
if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false,
|
||||||
|
"Has th -- legitimate table structures");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
|
||||||
|
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
|
||||||
|
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false,
|
||||||
|
"Has headers, scope, or abbr attribute -- legitimate table structures");
|
||||||
|
}
|
||||||
|
|
||||||
|
Accessible* cell = thisacc->Document()->GetAccessible(cellElm);
|
||||||
|
if (cell && cell->ChildCount() == 1 &&
|
||||||
|
cell->FirstChild()->IsAbbreviation()) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false,
|
||||||
|
"has abbr -- legitimate table structures");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for nested tables.
|
||||||
|
nsCOMPtr<nsIHTMLCollection> nestedTables =
|
||||||
|
el->GetElementsByTagName(NS_LITERAL_STRING("table"));
|
||||||
|
if (nestedTables->Length() > 0) {
|
||||||
|
RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If only 1 column or only 1 row, it's for layout.
|
||||||
|
auto colCount = ColCount();
|
||||||
|
if (colCount <= 1) {
|
||||||
|
RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
|
||||||
|
}
|
||||||
|
auto rowCount = RowCount();
|
||||||
|
if (rowCount <=1) {
|
||||||
|
RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for many columns.
|
||||||
|
if (colCount >= 5) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, ">=5 columns");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we know there are 2-4 columns and 2 or more rows. Check to see if
|
||||||
|
// there are visible borders on the cells.
|
||||||
|
// XXX currently, we just check the first cell -- do we really need to do more?
|
||||||
|
nsTableWrapperFrame* tableFrame = do_QueryFrame(el->GetPrimaryFrame());
|
||||||
|
if (!tableFrame) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "table with no frame!");
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
|
||||||
|
if (!cellFrame) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
|
||||||
|
}
|
||||||
|
|
||||||
|
nsMargin border;
|
||||||
|
cellFrame->GetXULBorder(border);
|
||||||
|
if (border.top && border.bottom && border.left && border.right) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rules for non-bordered tables with 2-4 columns and 2+ rows from here on
|
||||||
|
// forward.
|
||||||
|
|
||||||
|
// Check for styled background color across rows (alternating background
|
||||||
|
// color is a common feature for data tables).
|
||||||
|
auto childCount = thisacc->ChildCount();
|
||||||
|
nscolor rowColor = 0;
|
||||||
|
nscolor prevRowColor;
|
||||||
|
for (auto childIdx = 0U; childIdx < childCount; childIdx++) {
|
||||||
|
Accessible* child = thisacc->GetChildAt(childIdx);
|
||||||
|
if (child->IsHTMLTableRow()) {
|
||||||
|
prevRowColor = rowColor;
|
||||||
|
nsIFrame* rowFrame = child->GetFrame();
|
||||||
|
MOZ_ASSERT(rowFrame, "Table hierarchy got screwed up");
|
||||||
|
if (!rowFrame) {
|
||||||
|
RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy");
|
||||||
|
}
|
||||||
|
|
||||||
|
rowColor = rowFrame->StyleBackground()->BackgroundColor(rowFrame);
|
||||||
|
|
||||||
|
if (childIdx > 0 && prevRowColor != rowColor) {
|
||||||
|
RETURN_LAYOUT_ANSWER(
|
||||||
|
false, "2 styles of row background color, non-bordered"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for many rows.
|
||||||
|
const uint32_t kMaxLayoutRows = 20;
|
||||||
|
if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data
|
||||||
|
RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for very wide table.
|
||||||
|
nsIFrame* documentFrame = thisacc->Document()->GetFrame();
|
||||||
|
nsSize documentSize = documentFrame->GetSize();
|
||||||
|
if (documentSize.width > 0) {
|
||||||
|
nsSize tableSize = thisacc->GetFrame()->GetSize();
|
||||||
|
int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width;
|
||||||
|
if (percentageOfDocWidth > 95) {
|
||||||
|
// 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
|
||||||
|
// Probably for layout
|
||||||
|
RETURN_LAYOUT_ANSWER(
|
||||||
|
true, "<= 4 columns, table width is 95% of document width"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two column rules.
|
||||||
|
if (rowCount * colCount <= 10) {
|
||||||
|
RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const nsLiteralString tags[] = {
|
||||||
|
NS_LITERAL_STRING("embed"),
|
||||||
|
NS_LITERAL_STRING("object"),
|
||||||
|
NS_LITERAL_STRING("iframe")
|
||||||
|
};
|
||||||
|
for (auto& tag : tags) {
|
||||||
|
nsCOMPtr<nsIHTMLCollection> descendants = el->GetElementsByTagName(tag);
|
||||||
|
if (descendants->Length() > 0) {
|
||||||
|
RETURN_LAYOUT_ANSWER(
|
||||||
|
true, "Has no borders, and has iframe, object or embed, typical of advertisements"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_LAYOUT_ANSWER(
|
||||||
|
false, "No layout factor strong enough, so will guess data"
|
||||||
|
);
|
||||||
|
}
|
|
@ -173,7 +173,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Return true if the table is probably for layout.
|
* Return true if the table is probably for layout.
|
||||||
*/
|
*/
|
||||||
virtual bool IsProbablyLayoutTable() { return false; }
|
virtual bool IsProbablyLayoutTable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the table to an Accessible*.
|
* Convert the table to an Accessible*.
|
||||||
|
|
|
@ -21,6 +21,7 @@ UNIFIED_SOURCES += [
|
||||||
'ImageAccessible.cpp',
|
'ImageAccessible.cpp',
|
||||||
'OuterDocAccessible.cpp',
|
'OuterDocAccessible.cpp',
|
||||||
'RootAccessible.cpp',
|
'RootAccessible.cpp',
|
||||||
|
'TableAccessible.cpp',
|
||||||
'TableCellAccessible.cpp',
|
'TableCellAccessible.cpp',
|
||||||
'TextLeafAccessible.cpp',
|
'TextLeafAccessible.cpp',
|
||||||
]
|
]
|
||||||
|
|
|
@ -860,244 +860,6 @@ HTMLTableAccessible::Description(nsString& aDescription)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIHTMLCollection> elements =
|
|
||||||
mContent->AsElement()->GetElementsByTagName(aTagName);
|
|
||||||
|
|
||||||
Element* foundItem = elements->Item(0);
|
|
||||||
if (!foundItem)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (aAllowEmpty)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Make sure that the item we found has contents and either has multiple
|
|
||||||
// children or the found item is not a whitespace-only text node.
|
|
||||||
if (foundItem->GetChildCount() > 1)
|
|
||||||
return true; // Treat multiple child nodes as non-empty
|
|
||||||
|
|
||||||
nsIContent *innerItemContent = foundItem->GetFirstChild();
|
|
||||||
if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If we found more than one node then return true not depending on
|
|
||||||
// aAllowEmpty flag.
|
|
||||||
// XXX it might be dummy but bug 501375 where we changed this addresses
|
|
||||||
// performance problems only. Note, currently 'aAllowEmpty' flag is used for
|
|
||||||
// caption element only. On another hand we create accessible object for
|
|
||||||
// the first entry of caption element (see
|
|
||||||
// HTMLTableAccessible::InsertChildAt).
|
|
||||||
return !!elements->Item(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
HTMLTableAccessible::IsProbablyLayoutTable()
|
|
||||||
{
|
|
||||||
// Implement a heuristic to determine if table is most likely used for layout
|
|
||||||
// XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells
|
|
||||||
// at the beginning or end of a row/col, and especially when they occur at the edge of a table?
|
|
||||||
// XXX expose this info via object attributes to AT-SPI
|
|
||||||
|
|
||||||
// XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
|
|
||||||
// This will allow release trunk builds to be used by testers to refine the algorithm
|
|
||||||
// Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
|
|
||||||
#ifdef SHOW_LAYOUT_HEURISTIC
|
|
||||||
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
|
|
||||||
{ \
|
|
||||||
mLayoutHeuristic = isLayout ? \
|
|
||||||
NS_LITERAL_STRING("layout table: " heuristic) : \
|
|
||||||
NS_LITERAL_STRING("data table: " heuristic); \
|
|
||||||
return isLayout; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DocAccessible* docAccessible = Document();
|
|
||||||
if (docAccessible) {
|
|
||||||
uint64_t docState = docAccessible->State();
|
|
||||||
if (docState & states::EDITABLE) { // Need to see all elements while document is being edited
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "In editable document");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if an ARIA role overrides the role from native markup,
|
|
||||||
// but for which we still expose table semantics (treegrid, for example).
|
|
||||||
if (Role() != roles::TABLE)
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "Has role attribute");
|
|
||||||
|
|
||||||
if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
|
|
||||||
// Role attribute is present, but overridden roles have already been dealt with.
|
|
||||||
// Only landmarks and other roles that don't override the role from native
|
|
||||||
// markup are left to deal with here.
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_ASSERTION(mContent->IsHTMLElement(nsGkAtoms::table),
|
|
||||||
"table should not be built by CSS display:table style");
|
|
||||||
|
|
||||||
// Check if datatable attribute has "0" value.
|
|
||||||
if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
|
|
||||||
NS_LITERAL_STRING("0"), eCaseMatters)) {
|
|
||||||
RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for legitimate data table attributes.
|
|
||||||
nsAutoString summary;
|
|
||||||
if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
|
|
||||||
!summary.IsEmpty())
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
|
|
||||||
|
|
||||||
// Check for legitimate data table elements.
|
|
||||||
Accessible* caption = FirstChild();
|
|
||||||
if (caption && caption->Role() == roles::CAPTION && caption->HasChildren())
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
|
|
||||||
|
|
||||||
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
|
|
||||||
childElm = childElm->GetNextSibling()) {
|
|
||||||
if (!childElm->IsHTMLElement())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col,
|
|
||||||
nsGkAtoms::colgroup,
|
|
||||||
nsGkAtoms::tfoot,
|
|
||||||
nsGkAtoms::thead)) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false,
|
|
||||||
"Has col, colgroup, tfoot or thead -- legitimate table structures");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childElm->IsHTMLElement(nsGkAtoms::tbody)) {
|
|
||||||
for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
|
|
||||||
rowElm = rowElm->GetNextSibling()) {
|
|
||||||
if (rowElm->IsHTMLElement(nsGkAtoms::tr)) {
|
|
||||||
for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
|
|
||||||
cellElm = cellElm->GetNextSibling()) {
|
|
||||||
if (cellElm->IsHTMLElement()) {
|
|
||||||
|
|
||||||
if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false,
|
|
||||||
"Has th -- legitimate table structures");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
|
|
||||||
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
|
|
||||||
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false,
|
|
||||||
"Has headers, scope, or abbr attribute -- legitimate table structures");
|
|
||||||
}
|
|
||||||
|
|
||||||
Accessible* cell = mDoc->GetAccessible(cellElm);
|
|
||||||
if (cell && cell->ChildCount() == 1 &&
|
|
||||||
cell->FirstChild()->IsAbbreviation()) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false,
|
|
||||||
"has abbr -- legitimate table structures");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasDescendant(NS_LITERAL_STRING("table"))) {
|
|
||||||
RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only 1 column or only 1 row, it's for layout
|
|
||||||
uint32_t colCount = ColCount();
|
|
||||||
if (colCount <=1) {
|
|
||||||
RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
|
|
||||||
}
|
|
||||||
uint32_t rowCount = RowCount();
|
|
||||||
if (rowCount <=1) {
|
|
||||||
RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for many columns
|
|
||||||
if (colCount >= 5) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false, ">=5 columns");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we know there are 2-4 columns and 2 or more rows
|
|
||||||
// Check to see if there are visible borders on the cells
|
|
||||||
// XXX currently, we just check the first cell -- do we really need to do more?
|
|
||||||
nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
|
|
||||||
if (!tableFrame)
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "table with no frame!");
|
|
||||||
|
|
||||||
nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
|
|
||||||
if (!cellFrame)
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
|
|
||||||
|
|
||||||
nsMargin border;
|
|
||||||
cellFrame->GetXULBorder(border);
|
|
||||||
if (border.top && border.bottom && border.left && border.right) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Check for styled background color across rows (alternating background
|
|
||||||
// color is a common feature for data tables).
|
|
||||||
uint32_t childCount = ChildCount();
|
|
||||||
nscolor rowColor = 0;
|
|
||||||
nscolor prevRowColor;
|
|
||||||
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
|
|
||||||
Accessible* child = GetChildAt(childIdx);
|
|
||||||
if (child->Role() == roles::ROW) {
|
|
||||||
prevRowColor = rowColor;
|
|
||||||
nsIFrame* rowFrame = child->GetFrame();
|
|
||||||
MOZ_ASSERT(rowFrame, "Table hierarchy got screwed up");
|
|
||||||
if (!rowFrame) {
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy");
|
|
||||||
}
|
|
||||||
|
|
||||||
rowColor = rowFrame->StyleBackground()->BackgroundColor(rowFrame);
|
|
||||||
|
|
||||||
if (childIdx > 0 && prevRowColor != rowColor)
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for many rows
|
|
||||||
const uint32_t kMaxLayoutRows = 20;
|
|
||||||
if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data
|
|
||||||
RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for very wide table.
|
|
||||||
nsIFrame* documentFrame = Document()->GetFrame();
|
|
||||||
nsSize documentSize = documentFrame->GetSize();
|
|
||||||
if (documentSize.width > 0) {
|
|
||||||
nsSize tableSize = GetFrame()->GetSize();
|
|
||||||
int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width;
|
|
||||||
if (percentageOfDocWidth > 95) {
|
|
||||||
// 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
|
|
||||||
// Probably for layout
|
|
||||||
RETURN_LAYOUT_ANSWER(true,
|
|
||||||
"<= 4 columns, table width is 95% of document width");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Two column rules
|
|
||||||
if (rowCount * colCount <= 10) {
|
|
||||||
RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasDescendant(NS_LITERAL_STRING("embed")) ||
|
|
||||||
HasDescendant(NS_LITERAL_STRING("object")) ||
|
|
||||||
HasDescendant(NS_LITERAL_STRING("iframe"))) {
|
|
||||||
RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, or iframe, typical of advertisements");
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// HTMLCaptionAccessible
|
// HTMLCaptionAccessible
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -156,7 +156,6 @@ public:
|
||||||
virtual void SelectRow(uint32_t aRowIdx) override;
|
virtual void SelectRow(uint32_t aRowIdx) override;
|
||||||
virtual void UnselectCol(uint32_t aColIdx) override;
|
virtual void UnselectCol(uint32_t aColIdx) override;
|
||||||
virtual void UnselectRow(uint32_t aRowIdx) override;
|
virtual void UnselectRow(uint32_t aRowIdx) override;
|
||||||
virtual bool IsProbablyLayoutTable() override;
|
|
||||||
virtual Accessible* AsAccessible() override { return this; }
|
virtual Accessible* AsAccessible() override { return this; }
|
||||||
|
|
||||||
// Accessible
|
// Accessible
|
||||||
|
@ -199,15 +198,6 @@ protected:
|
||||||
TableSelection aTarget,
|
TableSelection aTarget,
|
||||||
bool aIsOuter);
|
bool aIsOuter);
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if table has an element with the given tag name.
|
|
||||||
*
|
|
||||||
* @param aTagName [in] tag name of searched element
|
|
||||||
* @param aAllowEmpty [in, optional] points if found element can be empty
|
|
||||||
* or contain whitespace text only.
|
|
||||||
*/
|
|
||||||
bool HasDescendant(const nsAString& aTagName, bool aAllowEmpty = true);
|
|
||||||
|
|
||||||
#ifdef SHOW_LAYOUT_HEURISTIC
|
#ifdef SHOW_LAYOUT_HEURISTIC
|
||||||
nsString mLayoutHeuristic;
|
nsString mLayoutHeuristic;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,106 +14,114 @@
|
||||||
src="../attributes.js"></script>
|
src="../attributes.js"></script>
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
function doTest() {
|
function isLayoutTable(id) {
|
||||||
// Attribute we're looking for
|
testAttrs(id, { "layout-guess": "true" }, true);
|
||||||
var attr = {
|
}
|
||||||
"layout-guess": "true"
|
function isDataTable(id) {
|
||||||
};
|
testAbsentAttrs(id, { "layout-guess": "true" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function doTest() {
|
||||||
// table with role of grid
|
// table with role of grid
|
||||||
testAbsentAttrs("table1", attr);
|
isDataTable("table1");
|
||||||
// table with role of grid and datatable="0"
|
// table with role of grid and datatable="0"
|
||||||
testAbsentAttrs("table1.1", attr);
|
isDataTable("table1.1");
|
||||||
|
|
||||||
// table with landmark role
|
// table with landmark role
|
||||||
testAbsentAttrs("table2", attr);
|
isDataTable("table2");
|
||||||
|
|
||||||
// table with summary
|
// table with summary
|
||||||
testAbsentAttrs("table3", attr);
|
isDataTable("table3");
|
||||||
|
|
||||||
// table with caption
|
// table with caption
|
||||||
testAbsentAttrs("table4", attr);
|
isDataTable("table4");
|
||||||
|
|
||||||
// layout table with empty caption
|
// layout table with empty caption
|
||||||
testAttrs("table4.2", attr, true);
|
isLayoutTable("table4.2");
|
||||||
|
|
||||||
// table with thead element
|
// table with thead element
|
||||||
testAbsentAttrs("table5", attr);
|
isDataTable("table5");
|
||||||
|
|
||||||
// table with tfoot element
|
// table with tfoot element
|
||||||
testAbsentAttrs("table5.1", attr);
|
isDataTable("table5.1");
|
||||||
|
|
||||||
// table with colgroup or col elements
|
// table with colgroup or col elements
|
||||||
testAbsentAttrs("table5.2", attr);
|
isDataTable("table5.2");
|
||||||
testAbsentAttrs("table5.3", attr);
|
isDataTable("table5.3");
|
||||||
|
|
||||||
// table with th element
|
// table with th element
|
||||||
testAbsentAttrs("table6", attr);
|
isDataTable("table6");
|
||||||
|
|
||||||
// table with headers attribute
|
// table with headers attribute
|
||||||
testAbsentAttrs("table6.2", attr);
|
isDataTable("table6.2");
|
||||||
|
|
||||||
// table with scope attribute
|
// table with scope attribute
|
||||||
testAbsentAttrs("table6.2.2", attr);
|
isDataTable("table6.2.2");
|
||||||
|
|
||||||
// table with abbr attribute
|
// table with abbr attribute
|
||||||
testAbsentAttrs("table6.2.3", attr);
|
isDataTable("table6.2.3");
|
||||||
|
|
||||||
// table with abbr element
|
// table with abbr element
|
||||||
testAbsentAttrs("table6.3", attr);
|
isDataTable("table6.3");
|
||||||
|
|
||||||
// table with abbr element having empty text node
|
// table with abbr element having empty text node
|
||||||
testAbsentAttrs("table6.4", attr);
|
isDataTable("table6.4");
|
||||||
|
|
||||||
// table with abbr element and non-empty text node
|
// table with abbr element and non-empty text node
|
||||||
testAttrs("table6.5", attr, true);
|
isLayoutTable("table6.5");
|
||||||
|
|
||||||
// layout table with nested table
|
// layout table with nested table
|
||||||
testAttrs("table9", attr, true);
|
isLayoutTable("table9");
|
||||||
|
|
||||||
// layout table with 1 column
|
// layout table with 1 column
|
||||||
testAttrs("table10", attr, true);
|
isLayoutTable("table10");
|
||||||
|
|
||||||
// layout table with 1 row
|
// layout table with 1 row
|
||||||
testAttrs("table11", attr, true);
|
isLayoutTable("table11");
|
||||||
|
|
||||||
// table with 5 columns
|
// table with 5 columns
|
||||||
testAbsentAttrs("table12", attr);
|
isDataTable("table12");
|
||||||
|
|
||||||
// table with a bordered cell
|
// table with a bordered cell
|
||||||
testAbsentAttrs("table13", attr);
|
isDataTable("table13");
|
||||||
|
|
||||||
// table with alternating row background colors
|
// table with alternating row background colors
|
||||||
testAbsentAttrs("table14", attr);
|
isDataTable("table14");
|
||||||
|
|
||||||
// table with 3 columns and 21 rows
|
// table with 3 columns and 21 rows
|
||||||
testAbsentAttrs("table15", attr);
|
isDataTable("table15");
|
||||||
|
|
||||||
// layout table that has a 100% width
|
// layout table that has a 100% width
|
||||||
testAttrs("table16", attr, true);
|
isLayoutTable("table16");
|
||||||
|
|
||||||
// layout table that has a 95% width in pixels
|
// layout table that has a 95% width in pixels
|
||||||
testAttrs("table17", attr, true);
|
isLayoutTable("table17");
|
||||||
|
|
||||||
// layout table with less than 10 columns
|
// layout table with less than 10 columns
|
||||||
testAttrs("table18", attr, true);
|
isLayoutTable("table18");
|
||||||
|
|
||||||
// layout table with embedded iframe
|
// layout table with embedded iframe
|
||||||
testAttrs("table19", attr, true);
|
isLayoutTable("table19");
|
||||||
|
|
||||||
// tree grid, no layout table
|
// tree grid, no layout table
|
||||||
testAbsentAttrs("table20", attr);
|
isDataTable("table20");
|
||||||
|
|
||||||
// layout table containing nested data table (having data structures)
|
// layout table containing nested data table (having data structures)
|
||||||
testAttrs("table21", attr, true);
|
isLayoutTable("table21");
|
||||||
testAttrs("table21.2", attr, true);
|
isLayoutTable("table21.2");
|
||||||
testAttrs("table21.3", attr, true);
|
isLayoutTable("table21.3");
|
||||||
testAttrs("table21.4", attr, true);
|
isLayoutTable("table21.4");
|
||||||
testAttrs("table21.5", attr, true);
|
isLayoutTable("table21.5");
|
||||||
testAttrs("table21.6", attr, true);
|
isLayoutTable("table21.6");
|
||||||
|
|
||||||
// layout table having datatable="0" attribute and containing data table structure (tfoot element)
|
// layout table having datatable="0" attribute and containing data table structure (tfoot element)
|
||||||
testAttrs("table22", attr, true);
|
isLayoutTable("table22");
|
||||||
|
|
||||||
|
// layout display:block table with 1 column
|
||||||
|
isLayoutTable("displayblock_table1");
|
||||||
|
|
||||||
|
// matrix
|
||||||
|
isDataTable("mtable1");
|
||||||
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
@ -501,5 +509,32 @@
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- display:block table -->
|
||||||
|
<table id="displayblock_table1" style="display:block">
|
||||||
|
<tr><td>Row1</td></tr>
|
||||||
|
<tr><td>Row2</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- MathML matrix -->
|
||||||
|
<math>
|
||||||
|
<mtable id="mtable1">
|
||||||
|
<mtr>
|
||||||
|
<mtd>
|
||||||
|
<mn>1</mn>
|
||||||
|
</mtd>
|
||||||
|
<mtd>
|
||||||
|
<mn>0</mn>
|
||||||
|
</mtd>
|
||||||
|
</mtr>
|
||||||
|
<mtr>
|
||||||
|
<mtd>
|
||||||
|
<mn>0</mn>
|
||||||
|
</mtd>
|
||||||
|
<mtd>
|
||||||
|
<mn>1</mn>
|
||||||
|
</mtd>
|
||||||
|
</mtr>
|
||||||
|
</mtable>
|
||||||
|
</math>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -213,21 +213,17 @@ OptionsPanel.prototype = {
|
||||||
"tools-not-supported-label");
|
"tools-not-supported-label");
|
||||||
let atleastOneToolNotSupported = false;
|
let atleastOneToolNotSupported = false;
|
||||||
|
|
||||||
const toolbox = this.toolbox;
|
|
||||||
|
|
||||||
// Signal tool registering/unregistering globally (for the tools registered
|
// Signal tool registering/unregistering globally (for the tools registered
|
||||||
// globally) and per toolbox (for the tools registered to a single toolbox).
|
// globally) and per toolbox (for the tools registered to a single toolbox).
|
||||||
// This event handler expect this to be binded to the related checkbox element.
|
// This event handler expect this to be binded to the related checkbox element.
|
||||||
let onCheckboxClick = function(tool) {
|
let onCheckboxClick = function(telemetry, tool) {
|
||||||
// Set the kill switch pref boolean to true
|
// Set the kill switch pref boolean to true
|
||||||
Services.prefs.setBoolPref(tool.visibilityswitch, this.checked);
|
Services.prefs.setBoolPref(tool.visibilityswitch, this.checked);
|
||||||
|
|
||||||
if (!tool.isWebExtension) {
|
if (!tool.isWebExtension) {
|
||||||
gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", tool.id);
|
gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", tool.id);
|
||||||
// Record which tools were registered and unregistered.
|
// Record which tools were registered and unregistered.
|
||||||
this.telemetry.keyedScalarSet("devtools.tool.registered",
|
telemetry.keyedScalarSet("devtools.tool.registered", tool.id, this.checked);
|
||||||
tool.id,
|
|
||||||
this.checked);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -253,7 +249,8 @@ OptionsPanel.prototype = {
|
||||||
checkboxInput.setAttribute("checked", "true");
|
checkboxInput.setAttribute("checked", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
checkboxInput.addEventListener("change", onCheckboxClick.bind(checkboxInput, tool));
|
checkboxInput.addEventListener("change",
|
||||||
|
onCheckboxClick.bind(checkboxInput, this.telemetry, tool));
|
||||||
|
|
||||||
checkboxLabel.appendChild(checkboxInput);
|
checkboxLabel.appendChild(checkboxInput);
|
||||||
checkboxLabel.appendChild(checkboxSpanLabel);
|
checkboxLabel.appendChild(checkboxSpanLabel);
|
||||||
|
@ -287,7 +284,7 @@ OptionsPanel.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populating the additional tools that came from the installed WebExtension add-ons.
|
// Populating the additional tools that came from the installed WebExtension add-ons.
|
||||||
for (let {uuid, name, pref} of toolbox.listWebExtensions()) {
|
for (let {uuid, name, pref} of this.toolbox.listWebExtensions()) {
|
||||||
atleastOneAddon = true;
|
atleastOneAddon = true;
|
||||||
|
|
||||||
additionalToolsBox.appendChild(createToolCheckbox({
|
additionalToolsBox.appendChild(createToolCheckbox({
|
||||||
|
|
|
@ -36,15 +36,10 @@ pref("devtools.inspector.enabled", true);
|
||||||
pref("devtools.inspector.activeSidebar", "ruleview");
|
pref("devtools.inspector.activeSidebar", "ruleview");
|
||||||
pref("devtools.inspector.remote", false);
|
pref("devtools.inspector.remote", false);
|
||||||
|
|
||||||
#if defined(NIGHTLY_BUILD)
|
|
||||||
// Show the 3 pane onboarding tooltip in the inspector
|
// Show the 3 pane onboarding tooltip in the inspector
|
||||||
pref("devtools.inspector.show-three-pane-tooltip", true);
|
pref("devtools.inspector.show-three-pane-tooltip", true);
|
||||||
// Enable the 3 pane mode in the inspector
|
// Enable the 3 pane mode in the inspector
|
||||||
pref("devtools.inspector.three-pane-enabled", true);
|
pref("devtools.inspector.three-pane-enabled", true);
|
||||||
#else
|
|
||||||
pref("devtools.inspector.show-three-pane-tooltip", false);
|
|
||||||
pref("devtools.inspector.three-pane-enabled", false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Collapse pseudo-elements by default in the rule-view
|
// Collapse pseudo-elements by default in the rule-view
|
||||||
pref("devtools.inspector.show_pseudo_elements", false);
|
pref("devtools.inspector.show_pseudo_elements", false);
|
||||||
|
|
|
@ -27,6 +27,7 @@ class Telemetry {
|
||||||
this.scalarSet = this.scalarSet.bind(this);
|
this.scalarSet = this.scalarSet.bind(this);
|
||||||
this.scalarAdd = this.scalarAdd.bind(this);
|
this.scalarAdd = this.scalarAdd.bind(this);
|
||||||
this.keyedScalarAdd = this.keyedScalarAdd.bind(this);
|
this.keyedScalarAdd = this.keyedScalarAdd.bind(this);
|
||||||
|
this.keyedScalarSet = this.keyedScalarSet.bind(this);
|
||||||
this.recordEvent = this.recordEvent.bind(this);
|
this.recordEvent = this.recordEvent.bind(this);
|
||||||
this.setEventRecordingEnabled = this.setEventRecordingEnabled.bind(this);
|
this.setEventRecordingEnabled = this.setEventRecordingEnabled.bind(this);
|
||||||
this.preparePendingEvent = this.preparePendingEvent.bind(this);
|
this.preparePendingEvent = this.preparePendingEvent.bind(this);
|
||||||
|
@ -246,6 +247,38 @@ class Telemetry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a value to a keyed scalar.
|
||||||
|
*
|
||||||
|
* @param {String} scalarId
|
||||||
|
* Scalar in which the data is to be stored.
|
||||||
|
* @param {String} key
|
||||||
|
* The key within the scalar.
|
||||||
|
* @param value
|
||||||
|
* Value to store.
|
||||||
|
*/
|
||||||
|
keyedScalarSet(scalarId, key, value) {
|
||||||
|
if (!scalarId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isNaN(value) && typeof value !== "boolean") {
|
||||||
|
dump(`Warning: An attempt was made to write a non-numeric and ` +
|
||||||
|
`non-boolean value ${value} to the ${scalarId} scalar. Only ` +
|
||||||
|
`numeric and boolean values are allowed.\n` +
|
||||||
|
`CALLER: ${getCaller()}`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Services.telemetry.keyedScalarSet(scalarId, key, value);
|
||||||
|
} catch (e) {
|
||||||
|
dump(`Warning: An attempt was made to write to the ${scalarId} ` +
|
||||||
|
`scalar, which is not defined in Scalars.yaml\n` +
|
||||||
|
`CALLER: ${getCaller()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a value to a keyed count scalar.
|
* Log a value to a keyed count scalar.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2039,7 +2039,7 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsDOMCSSAttributeDeclaration*
|
nsDOMCSSAttributeDeclaration*
|
||||||
Element::GetSMILOverrideStyle()
|
Element::SMILOverrideStyle()
|
||||||
{
|
{
|
||||||
Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
|
Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
|
||||||
|
|
||||||
|
@ -4489,7 +4489,7 @@ NoteDirtyElement(Element* aElement, uint32_t aBits)
|
||||||
// We can't check for a frame here, since <frame> elements inside <frameset>
|
// We can't check for a frame here, since <frame> elements inside <frameset>
|
||||||
// still need to generate a frame, even if they're display: none. :(
|
// still need to generate a frame, even if they're display: none. :(
|
||||||
//
|
//
|
||||||
// The servo traversal doesn't keep style data under display: none subtrees,
|
// The servo traversal doesn't keep style data under display: none subtrees,
|
||||||
// so in order for it to not need to cleanup each time anything happens in a
|
// so in order for it to not need to cleanup each time anything happens in a
|
||||||
// display: none subtree, we keep it clean.
|
// display: none subtree, we keep it clean.
|
||||||
//
|
//
|
||||||
|
|
|
@ -361,7 +361,7 @@ public:
|
||||||
* Note: This method is analogous to the 'GetStyle' method in
|
* Note: This method is analogous to the 'GetStyle' method in
|
||||||
* nsGenericHTMLElement and nsStyledElement.
|
* nsGenericHTMLElement and nsStyledElement.
|
||||||
*/
|
*/
|
||||||
nsDOMCSSAttributeDeclaration* GetSMILOverrideStyle();
|
nsDOMCSSAttributeDeclaration* SMILOverrideStyle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the element is labelable as per HTML specification.
|
* Returns if the element is labelable as per HTML specification.
|
||||||
|
|
|
@ -113,11 +113,6 @@ interface nsIImageLoadingContent : imgINotificationObserver
|
||||||
*/
|
*/
|
||||||
[notxpcom, nostdcall] void setBlockedRequest(in int16_t aContentDecision);
|
[notxpcom, nostdcall] void setBlockedRequest(in int16_t aContentDecision);
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the current request's size is available.
|
|
||||||
*/
|
|
||||||
[noscript, notxpcom] boolean currentRequestHasSize();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to notify the image loading content node that a frame has been
|
* Used to notify the image loading content node that a frame has been
|
||||||
* created.
|
* created.
|
||||||
|
|
|
@ -135,6 +135,10 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
||||||
int32_t aType,
|
int32_t aType,
|
||||||
const nsIntRect* aData)
|
const nsIntRect* aData)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(aRequest, "no request?");
|
||||||
|
MOZ_ASSERT(aRequest == mCurrentRequest || aRequest == mPendingRequest,
|
||||||
|
"Forgot to cancel a previous request?");
|
||||||
|
|
||||||
if (aType == imgINotificationObserver::IS_ANIMATED) {
|
if (aType == imgINotificationObserver::IS_ANIMATED) {
|
||||||
return OnImageIsAnimated(aRequest);
|
return OnImageIsAnimated(aRequest);
|
||||||
}
|
}
|
||||||
|
@ -144,14 +148,6 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
|
|
||||||
// We should definitely have a request here
|
|
||||||
MOZ_ASSERT(aRequest, "no request?");
|
|
||||||
|
|
||||||
MOZ_ASSERT(aRequest == mCurrentRequest || aRequest == mPendingRequest,
|
|
||||||
"Unknown request");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Calling Notify on observers can modify the list of observers so make
|
// Calling Notify on observers can modify the list of observers so make
|
||||||
// a local copy.
|
// a local copy.
|
||||||
|
@ -629,12 +625,6 @@ nsImageLoadingContent::GetRequest(int32_t aRequestType,
|
||||||
return result.StealNSResult();
|
return result.StealNSResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP_(bool)
|
|
||||||
nsImageLoadingContent::CurrentRequestHasSize()
|
|
||||||
{
|
|
||||||
return HaveSize(mCurrentRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP_(void)
|
NS_IMETHODIMP_(void)
|
||||||
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
|
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
|
||||||
{
|
{
|
||||||
|
|
|
@ -521,7 +521,8 @@ skip-if = toolkit == 'android' #bug 687032
|
||||||
[test_bug698381.html]
|
[test_bug698381.html]
|
||||||
[test_bug698384.html]
|
[test_bug698384.html]
|
||||||
[test_bug704063.html]
|
[test_bug704063.html]
|
||||||
[test_bug704320.html]
|
[test_bug704320-1.html]
|
||||||
|
[test_bug704320-2.html]
|
||||||
[test_bug704320_policyset.html]
|
[test_bug704320_policyset.html]
|
||||||
[test_bug704320_policyset2.html]
|
[test_bug704320_policyset2.html]
|
||||||
[test_bug704320_preload.html]
|
[test_bug704320_preload.html]
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=704320
|
||||||
|
This Test is split into two for Bug 1453396
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 704320-Part1</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<script type="application/javascript" src="referrerHelper.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
//generates URLs to test
|
||||||
|
var generateURLArray = (function(from, to){
|
||||||
|
const baseURL = '://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=';
|
||||||
|
const schemeTo = '&scheme-to=';
|
||||||
|
|
||||||
|
return [
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=no-referrer-when-downgrade',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=no-referrer',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=unsafe-url',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=origin',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=origin-when-cross-origin',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=same-origin',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=strict-origin',
|
||||||
|
from + baseURL + from + schemeTo + to + '&policy=strict-origin-when-cross-origin',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
let testIframeUrls = [generateURLArray('http', 'http'),
|
||||||
|
generateURLArray('https', 'https')];
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
let advance = function(testName) {
|
||||||
|
testsGenerator[testName].next();
|
||||||
|
};
|
||||||
|
|
||||||
|
let testNames = ['testframeone', 'testframetwo'];
|
||||||
|
let isTestFinished = 0;
|
||||||
|
|
||||||
|
function checkTestsCompleted() {
|
||||||
|
isTestFinished++;
|
||||||
|
if (isTestFinished == 2) {
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let testsGenerator = {};
|
||||||
|
SimpleTest.requestLongerTimeout(4);
|
||||||
|
/**
|
||||||
|
* This is the main test routine -- serialized by use of a generator.
|
||||||
|
* It performs all tests in sequence using four iframes.
|
||||||
|
*/
|
||||||
|
function startTests(testName, testIframeUrls) {
|
||||||
|
testsGenerator[testName] = (function*() {
|
||||||
|
var testframe = document.getElementById(testName);
|
||||||
|
testframe.onload = function() {
|
||||||
|
advance(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the test frame from testIframeUrls[url]
|
||||||
|
// it will call back into this function via postMessage when it finishes
|
||||||
|
// loading and continue beyond the yield.
|
||||||
|
for(url in testIframeUrls) {
|
||||||
|
yield testframe.src = testIframeUrls[url];
|
||||||
|
// run test and check result for loaded test URL
|
||||||
|
yield checkExpectedGlobalResults(testName);
|
||||||
|
}
|
||||||
|
checkTestsCompleted();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < testIframeUrls.length; i++) {
|
||||||
|
startTests(testNames[i], testIframeUrls[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="testsGenerator[testNames[0]].next();
|
||||||
|
testsGenerator[testNames[1]].next();">
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=704320">Mozilla Bug 704320 - HTTP/HTTPS to HTTPS/HTTP</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<pre id="content">
|
||||||
|
</pre>
|
||||||
|
<iframe id="testframeone"></iframe>
|
||||||
|
<iframe id="testframetwo"></iframe>
|
||||||
|
</body>
|
|
@ -2,10 +2,11 @@
|
||||||
<html>
|
<html>
|
||||||
<!--
|
<!--
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=704320
|
https://bugzilla.mozilla.org/show_bug.cgi?id=704320
|
||||||
|
This Test is split into two for Bug 1453396
|
||||||
-->
|
-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Test for Bug 704320</title>
|
<title>Test for Bug 704320-Part2</title>
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
<script type="application/javascript" src="referrerHelper.js"></script>
|
<script type="application/javascript" src="referrerHelper.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
@ -29,9 +30,7 @@ var generateURLArray = (function(from, to){
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
let testIframeUrls = [generateURLArray('http', 'http'),
|
let testIframeUrls = [generateURLArray('http', 'https'),
|
||||||
generateURLArray('https', 'https'),
|
|
||||||
generateURLArray('http', 'https'),
|
|
||||||
generateURLArray('https', 'http')];
|
generateURLArray('https', 'http')];
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
@ -39,13 +38,12 @@ let advance = function(testName) {
|
||||||
testsGenerator[testName].next();
|
testsGenerator[testName].next();
|
||||||
};
|
};
|
||||||
|
|
||||||
let testNames = ['testframeone', 'testframetwo', 'testframethree',
|
let testNames = ['testframeone', 'testframetwo'];
|
||||||
'testframefour'];
|
|
||||||
let isTestFinished = 0;
|
let isTestFinished = 0;
|
||||||
|
|
||||||
function checkTestsCompleted() {
|
function checkTestsCompleted() {
|
||||||
isTestFinished++;
|
isTestFinished++;
|
||||||
if (isTestFinished == 4) {
|
if (isTestFinished == 2) {
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,15 +80,11 @@ for (i = 0; i < testIframeUrls.length; i++) {
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="testsGenerator[testNames[0]].next();
|
<body onload="testsGenerator[testNames[0]].next();
|
||||||
testsGenerator[testNames[1]].next();
|
testsGenerator[testNames[1]].next();">
|
||||||
testsGenerator[testNames[2]].next();
|
|
||||||
testsGenerator[testNames[3]].next();">
|
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=704320">Mozilla Bug 704320 - HTTP/HTTPS to HTTPS/HTTP</a>
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=704320">Mozilla Bug 704320 - HTTP/HTTPS to HTTPS/HTTP</a>
|
||||||
<p id="display"></p>
|
<p id="display"></p>
|
||||||
<pre id="content">
|
<pre id="content">
|
||||||
</pre>
|
</pre>
|
||||||
<iframe id="testframeone"></iframe>
|
<iframe id="testframeone"></iframe>
|
||||||
<iframe id="testframetwo"></iframe>
|
<iframe id="testframetwo"></iframe>
|
||||||
<iframe id="testframethree"></iframe>
|
|
||||||
<iframe id="testframefour"></iframe>
|
|
||||||
</body>
|
</body>
|
|
@ -481,7 +481,9 @@ public:
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
|
||||||
using nsINamed::GetName;
|
using nsINamed::GetName;
|
||||||
|
#endif
|
||||||
|
|
||||||
// nsIAsyncShutdownBlocker interface
|
// nsIAsyncShutdownBlocker interface
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "nsSize.h"
|
#include "nsSize.h"
|
||||||
#include "nsDocument.h"
|
#include "nsDocument.h"
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
|
#include "nsImageFrame.h"
|
||||||
#include "nsIScriptContext.h"
|
#include "nsIScriptContext.h"
|
||||||
#include "nsIURL.h"
|
#include "nsIURL.h"
|
||||||
#include "nsIIOService.h"
|
#include "nsIIOService.h"
|
||||||
|
@ -949,7 +950,7 @@ HTMLImageElement::InResponsiveMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSelectedDensity)
|
HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource)
|
||||||
{
|
{
|
||||||
// If there was no selected source previously, we don't want to short-circuit the load.
|
// If there was no selected source previously, we don't want to short-circuit the load.
|
||||||
// Similarly for if there is no newly selected source.
|
// Similarly for if there is no newly selected source.
|
||||||
|
@ -957,32 +958,58 @@ HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSel
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool equal = false;
|
bool equal = false;
|
||||||
return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) && equal &&
|
return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) && equal;
|
||||||
aSelectedDensity == mCurrentDensity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
|
HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
|
||||||
{
|
{
|
||||||
nsresult rv = NS_ERROR_FAILURE;
|
double currentDensity = 1.0; // default to 1.0 for the src attribute case
|
||||||
|
|
||||||
|
// Helper to update state when only density may have changed (i.e., the source
|
||||||
|
// to load hasn't changed, and we don't do any request at all). We need (apart
|
||||||
|
// from updating our internal state) to tell the image frame because its
|
||||||
|
// intrinsic size may have changed.
|
||||||
|
//
|
||||||
|
// In the case we actually trigger a new load, that load will trigger a call
|
||||||
|
// to nsImageFrame::NotifyNewCurrentRequest, which takes care of that for us.
|
||||||
|
auto UpdateDensityOnly = [&]() -> void {
|
||||||
|
if (mCurrentDensity == currentDensity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mCurrentDensity = currentDensity;
|
||||||
|
if (nsImageFrame* f = do_QueryFrame(GetPrimaryFrame())) {
|
||||||
|
f->ResponsiveContentDensityChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (aForce) {
|
if (aForce) {
|
||||||
// In responsive mode we generally want to re-run the full
|
// In responsive mode we generally want to re-run the full selection
|
||||||
// selection algorithm whenever starting a new load, per
|
// algorithm whenever starting a new load, per spec.
|
||||||
// spec. This also causes us to re-resolve the URI as appropriate.
|
//
|
||||||
if (!UpdateResponsiveSource() && !aAlwaysLoad) {
|
// This also causes us to re-resolve the URI as appropriate.
|
||||||
|
const bool sourceChanged = UpdateResponsiveSource();
|
||||||
|
|
||||||
|
if (mResponsiveSelector) {
|
||||||
|
currentDensity = mResponsiveSelector->GetSelectedImageDensity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceChanged && !aAlwaysLoad) {
|
||||||
|
UpdateDensityOnly();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
} else if (mResponsiveSelector) {
|
||||||
|
currentDensity = mResponsiveSelector->GetSelectedImageDensity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult rv = NS_ERROR_FAILURE;
|
||||||
nsCOMPtr<nsIURI> selectedSource;
|
nsCOMPtr<nsIURI> selectedSource;
|
||||||
double currentDensity = 1.0; // default to 1.0 for the src attribute case
|
|
||||||
if (mResponsiveSelector) {
|
if (mResponsiveSelector) {
|
||||||
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
|
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
|
||||||
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mResponsiveSelector->GetSelectedImageTriggeringPrincipal();
|
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mResponsiveSelector->GetSelectedImageTriggeringPrincipal();
|
||||||
selectedSource = url;
|
selectedSource = url;
|
||||||
currentDensity = mResponsiveSelector->GetSelectedImageDensity();
|
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource)) {
|
||||||
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
|
UpdateDensityOnly();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
if (url) {
|
if (url) {
|
||||||
|
@ -995,12 +1022,11 @@ HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
|
||||||
CancelImageRequests(aNotify);
|
CancelImageRequests(aNotify);
|
||||||
rv = NS_OK;
|
rv = NS_OK;
|
||||||
} else {
|
} else {
|
||||||
nsIDocument* doc = GetOurOwnerDoc();
|
nsIDocument* doc = OwnerDoc();
|
||||||
if (doc) {
|
StringToURI(src, doc, getter_AddRefs(selectedSource));
|
||||||
StringToURI(src, doc, getter_AddRefs(selectedSource));
|
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource)) {
|
||||||
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
|
UpdateDensityOnly();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a srcset attribute or are in a <picture> element,
|
// If we have a srcset attribute or are in a <picture> element,
|
||||||
|
|
|
@ -43,6 +43,11 @@ public:
|
||||||
|
|
||||||
virtual bool Draggable() const override;
|
virtual bool Draggable() const override;
|
||||||
|
|
||||||
|
ResponsiveImageSelector* GetResponsiveImageSelector()
|
||||||
|
{
|
||||||
|
return mResponsiveSelector.get();
|
||||||
|
}
|
||||||
|
|
||||||
// Element
|
// Element
|
||||||
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
|
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
|
||||||
|
|
||||||
|
@ -325,8 +330,8 @@ protected:
|
||||||
// only mode after Bug 1076583
|
// only mode after Bug 1076583
|
||||||
bool InResponsiveMode();
|
bool InResponsiveMode();
|
||||||
|
|
||||||
// True if the given URL and density equal the last URL and density that was loaded by this element.
|
// True if the given URL equals the last URL that was loaded by this element.
|
||||||
bool SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSelectedDensity);
|
bool SelectedSourceMatchesLast(nsIURI* aSelectedSource);
|
||||||
|
|
||||||
// Resolve and load the current mResponsiveSelector (responsive mode) or src
|
// Resolve and load the current mResponsiveSelector (responsive mode) or src
|
||||||
// attr image.
|
// attr image.
|
||||||
|
|
|
@ -902,6 +902,10 @@ nsPluginInstanceOwner::RequestCommitOrCancel(bool aCommitted)
|
||||||
bool
|
bool
|
||||||
nsPluginInstanceOwner::EnableIME(bool aEnable)
|
nsPluginInstanceOwner::EnableIME(bool aEnable)
|
||||||
{
|
{
|
||||||
|
if (NS_WARN_IF(!mPluginFrame)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
|
nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
widget = GetRootWidgetForPluginFrame(mPluginFrame);
|
widget = GetRootWidgetForPluginFrame(mPluginFrame);
|
||||||
|
|
|
@ -107,17 +107,14 @@ nsresult
|
||||||
nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue)
|
nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
|
||||||
return mElement->GetSMILOverrideStyle()->SetSMILValue(mPropID, aValue);
|
return mElement->SMILOverrideStyle()->SetSMILValue(mPropID, aValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsSMILCSSProperty::ClearAnimValue()
|
nsSMILCSSProperty::ClearAnimValue()
|
||||||
{
|
{
|
||||||
// Put empty string in override style for our property
|
// Put empty string in override style for our property
|
||||||
nsDOMCSSAttributeDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
|
mElement->SMILOverrideStyle()->SetPropertyValue(mPropID, EmptyString(), nullptr);
|
||||||
if (overrideDecl) {
|
|
||||||
overrideDecl->SetPropertyValue(mPropID, EmptyString(), nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on http://www.w3.org/TR/SVG/propidx.html
|
// Based on http://www.w3.org/TR/SVG/propidx.html
|
||||||
|
|
|
@ -564,7 +564,12 @@ static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) {
|
||||||
// Bias used for conservative rounding of float rects to int rects, to nudge the irects a little
|
// Bias used for conservative rounding of float rects to int rects, to nudge the irects a little
|
||||||
// larger, so we don't "think" a path's bounds are inside a clip, when (due to numeric drift in
|
// larger, so we don't "think" a path's bounds are inside a clip, when (due to numeric drift in
|
||||||
// the scan-converter) we might walk beyond the predicted limits.
|
// the scan-converter) we might walk beyond the predicted limits.
|
||||||
static const double kConservativeRoundBias = 0.5 + 0.5 / SK_FDot6One;
|
//
|
||||||
|
// This value has been determined trial and error: pick the smallest value (after the 0.5) that
|
||||||
|
// fixes any problematic cases (e.g. crbug.com/844457)
|
||||||
|
// NOTE: cubics appear to be the main reason for needing this slop. If we could (perhaps) have a
|
||||||
|
// more accurate walker for cubics, we may be able to reduce this fudge factor.
|
||||||
|
static const double kConservativeRoundBias = 0.5 + 1.5 / SK_FDot6One;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Round the value down. This is used to round the top and left of a rectangle,
|
* Round the value down. This is used to round the top and left of a rectangle,
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "mozilla/Likely.h"
|
#include "mozilla/Likely.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
|
#include "mozilla/Tuple.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozilla/Variant.h"
|
#include "mozilla/Variant.h"
|
||||||
|
@ -174,6 +175,43 @@ public:
|
||||||
return *result;
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write pixels to the surface by calling a lambda which may write as many
|
||||||
|
* pixels as there is remaining to complete the row. It is not completely
|
||||||
|
* memory safe as it trusts the underlying decoder not to overrun the given
|
||||||
|
* buffer, however it is an acceptable tradeoff for performance.
|
||||||
|
*
|
||||||
|
* Writing continues until every pixel in the surface has been written to
|
||||||
|
* (i.e., IsSurfaceFinished() returns true) or the lambda returns a WriteState
|
||||||
|
* which WritePixelBlocks() will return to the caller.
|
||||||
|
*
|
||||||
|
* The template parameter PixelType must be uint8_t (for paletted surfaces) or
|
||||||
|
* uint32_t (for BGRA/BGRX surfaces) and must be in agreement with the pixel
|
||||||
|
* size passed to ConfigureFilter().
|
||||||
|
*
|
||||||
|
* XXX(seth): We'll remove all support for paletted surfaces in bug 1247520,
|
||||||
|
* which means we can remove the PixelType template parameter from this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param aFunc A lambda that functions as a generator, yielding at most the
|
||||||
|
* maximum number of pixels requested. The lambda must accept a
|
||||||
|
* pointer argument to the first pixel to write, a maximum
|
||||||
|
* number of pixels to write as part of the block, and return a
|
||||||
|
* NextPixel<PixelType> value.
|
||||||
|
*
|
||||||
|
* @return A WriteState value indicating the lambda generator's state.
|
||||||
|
* WritePixelBlocks() itself will return WriteState::FINISHED if
|
||||||
|
* writing has finished, regardless of the lambda's internal state.
|
||||||
|
*/
|
||||||
|
template <typename PixelType, typename Func>
|
||||||
|
WriteState WritePixelBlocks(Func aFunc)
|
||||||
|
{
|
||||||
|
Maybe<WriteState> result;
|
||||||
|
while (!(result = DoWritePixelBlockToRow<PixelType>(Forward<Func>(aFunc)))) { }
|
||||||
|
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A variant of WritePixels() that writes a single row of pixels to the
|
* A variant of WritePixels() that writes a single row of pixels to the
|
||||||
* surface one at a time by repeatedly calling a lambda that yields pixels.
|
* surface one at a time by repeatedly calling a lambda that yields pixels.
|
||||||
|
@ -448,6 +486,50 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal method used to implement WritePixelBlocks. This method writes
|
||||||
|
* up to the number of pixels necessary to complete the row and returns Some()
|
||||||
|
* if we either finished the entire surface or the lambda returned a
|
||||||
|
* WriteState indicating that we should return to the caller. If the row was
|
||||||
|
* successfully written without either of those things happening, it returns
|
||||||
|
* Nothing(), allowing WritePixelBlocks() to iterate to fill as many rows as
|
||||||
|
* possible.
|
||||||
|
*/
|
||||||
|
template <typename PixelType, typename Func>
|
||||||
|
Maybe<WriteState> DoWritePixelBlockToRow(Func aFunc)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mPixelSize == 1 || mPixelSize == 4);
|
||||||
|
MOZ_ASSERT_IF(mPixelSize == 1, sizeof(PixelType) == sizeof(uint8_t));
|
||||||
|
MOZ_ASSERT_IF(mPixelSize == 4, sizeof(PixelType) == sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (IsSurfaceFinished()) {
|
||||||
|
return Some(WriteState::FINISHED); // We're already done.
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelType* rowPtr = reinterpret_cast<PixelType*>(mRowPointer);
|
||||||
|
int32_t remainder = mInputSize.width - mCol;
|
||||||
|
int32_t written;
|
||||||
|
Maybe<WriteState> result;
|
||||||
|
Tie(written, result) = aFunc(&rowPtr[mCol], remainder);
|
||||||
|
if (written == remainder) {
|
||||||
|
MOZ_ASSERT(result.isNothing());
|
||||||
|
mCol = mInputSize.width;
|
||||||
|
AdvanceRow(); // We've finished the row.
|
||||||
|
return IsSurfaceFinished() ? Some(WriteState::FINISHED)
|
||||||
|
: Nothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(written >= 0 && written < remainder);
|
||||||
|
MOZ_ASSERT(result.isSome());
|
||||||
|
|
||||||
|
mCol += written;
|
||||||
|
if (*result == WriteState::FINISHED) {
|
||||||
|
ZeroOutRestOfSurface<PixelType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An internal method used to implement both WritePixels() and
|
* An internal method used to implement both WritePixels() and
|
||||||
* WritePixelsToRow(). Those methods differ only in their behavior after a row
|
* WritePixelsToRow(). Those methods differ only in their behavior after a row
|
||||||
|
@ -558,6 +640,20 @@ public:
|
||||||
return mHead->WritePixels<PixelType>(Forward<Func>(aFunc));
|
return mHead->WritePixels<PixelType>(Forward<Func>(aFunc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variant of WritePixels() that writes up to a single row of pixels to the
|
||||||
|
* surface in blocks by repeatedly calling a lambda that yields up to the
|
||||||
|
* requested number of pixels.
|
||||||
|
*
|
||||||
|
* @see SurfaceFilter::WritePixelBlocks() for the canonical documentation.
|
||||||
|
*/
|
||||||
|
template <typename PixelType, typename Func>
|
||||||
|
WriteState WritePixelBlocks(Func aFunc)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mHead, "Use before configured!");
|
||||||
|
return mHead->WritePixelBlocks<PixelType>(Forward<Func>(aFunc));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A variant of WritePixels() that writes a single row of pixels to the
|
* A variant of WritePixels() that writes a single row of pixels to the
|
||||||
* surface one at a time by repeatedly calling a lambda that yields pixels.
|
* surface one at a time by repeatedly calling a lambda that yields pixels.
|
||||||
|
|
|
@ -292,10 +292,12 @@ nsGIFDecoder2::ColormapIndexToPixel<uint8_t>(uint8_t aIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename PixelSize>
|
template <typename PixelSize>
|
||||||
NextPixel<PixelSize>
|
Tuple<int32_t, Maybe<WriteState>>
|
||||||
nsGIFDecoder2::YieldPixel(const uint8_t* aData,
|
nsGIFDecoder2::YieldPixels(const uint8_t* aData,
|
||||||
size_t aLength,
|
size_t aLength,
|
||||||
size_t* aBytesReadOut)
|
size_t* aBytesReadOut,
|
||||||
|
PixelSize* aPixelBlock,
|
||||||
|
int32_t aBlockSize)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aData);
|
MOZ_ASSERT(aData);
|
||||||
MOZ_ASSERT(aBytesReadOut);
|
MOZ_ASSERT(aBytesReadOut);
|
||||||
|
@ -304,108 +306,119 @@ nsGIFDecoder2::YieldPixel(const uint8_t* aData,
|
||||||
// Advance to the next byte we should read.
|
// Advance to the next byte we should read.
|
||||||
const uint8_t* data = aData + *aBytesReadOut;
|
const uint8_t* data = aData + *aBytesReadOut;
|
||||||
|
|
||||||
// If we don't have any decoded data to yield, try to read some input and
|
int32_t written = 0;
|
||||||
// produce some.
|
while (aBlockSize > written) {
|
||||||
if (mGIFStruct.stackp == mGIFStruct.stack) {
|
// If we don't have any decoded data to yield, try to read some input and
|
||||||
while (mGIFStruct.bits < mGIFStruct.codesize && *aBytesReadOut < aLength) {
|
// produce some.
|
||||||
// Feed the next byte into the decoder's 32-bit input buffer.
|
if (mGIFStruct.stackp == mGIFStruct.stack) {
|
||||||
mGIFStruct.datum += int32_t(*data) << mGIFStruct.bits;
|
while (mGIFStruct.bits < mGIFStruct.codesize && *aBytesReadOut < aLength) {
|
||||||
mGIFStruct.bits += 8;
|
// Feed the next byte into the decoder's 32-bit input buffer.
|
||||||
data += 1;
|
mGIFStruct.datum += int32_t(*data) << mGIFStruct.bits;
|
||||||
*aBytesReadOut += 1;
|
mGIFStruct.bits += 8;
|
||||||
}
|
data += 1;
|
||||||
|
*aBytesReadOut += 1;
|
||||||
if (mGIFStruct.bits < mGIFStruct.codesize) {
|
|
||||||
return AsVariant(WriteState::NEED_MORE_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the leading variable-length symbol from the data stream.
|
|
||||||
int code = mGIFStruct.datum & mGIFStruct.codemask;
|
|
||||||
mGIFStruct.datum >>= mGIFStruct.codesize;
|
|
||||||
mGIFStruct.bits -= mGIFStruct.codesize;
|
|
||||||
|
|
||||||
const int clearCode = ClearCode();
|
|
||||||
|
|
||||||
// Reset the dictionary to its original state, if requested
|
|
||||||
if (code == clearCode) {
|
|
||||||
mGIFStruct.codesize = mGIFStruct.datasize + 1;
|
|
||||||
mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
|
|
||||||
mGIFStruct.avail = clearCode + 2;
|
|
||||||
mGIFStruct.oldcode = -1;
|
|
||||||
return AsVariant(WriteState::NEED_MORE_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for explicit end-of-stream code. It should only appear after all
|
|
||||||
// image data, but if that was the case we wouldn't be in this function, so
|
|
||||||
// this is always an error condition.
|
|
||||||
if (code == (clearCode + 1)) {
|
|
||||||
return AsVariant(WriteState::FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mGIFStruct.oldcode == -1) {
|
|
||||||
if (code >= MAX_BITS) {
|
|
||||||
return AsVariant(WriteState::FAILURE); // The code's too big; something's wrong.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mGIFStruct.firstchar = mGIFStruct.oldcode = code;
|
if (mGIFStruct.bits < mGIFStruct.codesize) {
|
||||||
|
return MakeTuple(written, Some(WriteState::NEED_MORE_DATA));
|
||||||
// Yield a pixel at the appropriate index in the colormap.
|
|
||||||
mGIFStruct.pixels_remaining--;
|
|
||||||
return AsVariant(ColormapIndexToPixel<PixelSize>(mGIFStruct.suffix[code]));
|
|
||||||
}
|
|
||||||
|
|
||||||
int incode = code;
|
|
||||||
if (code >= mGIFStruct.avail) {
|
|
||||||
*mGIFStruct.stackp++ = mGIFStruct.firstchar;
|
|
||||||
code = mGIFStruct.oldcode;
|
|
||||||
|
|
||||||
if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
|
|
||||||
return AsVariant(WriteState::FAILURE); // Stack overflow; something's wrong.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (code >= clearCode) {
|
|
||||||
if ((code >= MAX_BITS) || (code == mGIFStruct.prefix[code])) {
|
|
||||||
return AsVariant(WriteState::FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*mGIFStruct.stackp++ = mGIFStruct.suffix[code];
|
// Get the leading variable-length symbol from the data stream.
|
||||||
code = mGIFStruct.prefix[code];
|
int code = mGIFStruct.datum & mGIFStruct.codemask;
|
||||||
|
mGIFStruct.datum >>= mGIFStruct.codesize;
|
||||||
|
mGIFStruct.bits -= mGIFStruct.codesize;
|
||||||
|
|
||||||
if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
|
const int clearCode = ClearCode();
|
||||||
return AsVariant(WriteState::FAILURE); // Stack overflow; something's wrong.
|
|
||||||
|
// Reset the dictionary to its original state, if requested
|
||||||
|
if (code == clearCode) {
|
||||||
|
mGIFStruct.codesize = mGIFStruct.datasize + 1;
|
||||||
|
mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
|
||||||
|
mGIFStruct.avail = clearCode + 2;
|
||||||
|
mGIFStruct.oldcode = -1;
|
||||||
|
return MakeTuple(written, Some(WriteState::NEED_MORE_DATA));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for explicit end-of-stream code. It should only appear after all
|
||||||
|
// image data, but if that was the case we wouldn't be in this function, so
|
||||||
|
// this is always an error condition.
|
||||||
|
if (code == (clearCode + 1)) {
|
||||||
|
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mGIFStruct.oldcode == -1) {
|
||||||
|
if (code >= MAX_BITS) {
|
||||||
|
// The code's too big; something's wrong.
|
||||||
|
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||||
|
}
|
||||||
|
|
||||||
|
mGIFStruct.firstchar = mGIFStruct.oldcode = code;
|
||||||
|
|
||||||
|
// Yield a pixel at the appropriate index in the colormap.
|
||||||
|
mGIFStruct.pixels_remaining--;
|
||||||
|
aPixelBlock[written++] =
|
||||||
|
ColormapIndexToPixel<PixelSize>(mGIFStruct.suffix[code]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int incode = code;
|
||||||
|
if (code >= mGIFStruct.avail) {
|
||||||
|
*mGIFStruct.stackp++ = mGIFStruct.firstchar;
|
||||||
|
code = mGIFStruct.oldcode;
|
||||||
|
|
||||||
|
if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
|
||||||
|
// Stack overflow; something's wrong.
|
||||||
|
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (code >= clearCode) {
|
||||||
|
if ((code >= MAX_BITS) || (code == mGIFStruct.prefix[code])) {
|
||||||
|
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||||
|
}
|
||||||
|
|
||||||
|
*mGIFStruct.stackp++ = mGIFStruct.suffix[code];
|
||||||
|
code = mGIFStruct.prefix[code];
|
||||||
|
|
||||||
|
if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
|
||||||
|
// Stack overflow; something's wrong.
|
||||||
|
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*mGIFStruct.stackp++ = mGIFStruct.firstchar = mGIFStruct.suffix[code];
|
||||||
|
|
||||||
|
// Define a new codeword in the dictionary.
|
||||||
|
if (mGIFStruct.avail < 4096) {
|
||||||
|
mGIFStruct.prefix[mGIFStruct.avail] = mGIFStruct.oldcode;
|
||||||
|
mGIFStruct.suffix[mGIFStruct.avail] = mGIFStruct.firstchar;
|
||||||
|
mGIFStruct.avail++;
|
||||||
|
|
||||||
|
// If we've used up all the codewords of a given length increase the
|
||||||
|
// length of codewords by one bit, but don't exceed the specified maximum
|
||||||
|
// codeword size of 12 bits.
|
||||||
|
if (((mGIFStruct.avail & mGIFStruct.codemask) == 0) &&
|
||||||
|
(mGIFStruct.avail < 4096)) {
|
||||||
|
mGIFStruct.codesize++;
|
||||||
|
mGIFStruct.codemask += mGIFStruct.avail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mGIFStruct.oldcode = incode;
|
||||||
}
|
}
|
||||||
|
|
||||||
*mGIFStruct.stackp++ = mGIFStruct.firstchar = mGIFStruct.suffix[code];
|
if (MOZ_UNLIKELY(mGIFStruct.stackp <= mGIFStruct.stack)) {
|
||||||
|
MOZ_ASSERT_UNREACHABLE("No decoded data but we didn't return early?");
|
||||||
// Define a new codeword in the dictionary.
|
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||||
if (mGIFStruct.avail < 4096) {
|
|
||||||
mGIFStruct.prefix[mGIFStruct.avail] = mGIFStruct.oldcode;
|
|
||||||
mGIFStruct.suffix[mGIFStruct.avail] = mGIFStruct.firstchar;
|
|
||||||
mGIFStruct.avail++;
|
|
||||||
|
|
||||||
// If we've used up all the codewords of a given length increase the
|
|
||||||
// length of codewords by one bit, but don't exceed the specified maximum
|
|
||||||
// codeword size of 12 bits.
|
|
||||||
if (((mGIFStruct.avail & mGIFStruct.codemask) == 0) &&
|
|
||||||
(mGIFStruct.avail < 4096)) {
|
|
||||||
mGIFStruct.codesize++;
|
|
||||||
mGIFStruct.codemask += mGIFStruct.avail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mGIFStruct.oldcode = incode;
|
// Yield a pixel at the appropriate index in the colormap.
|
||||||
|
mGIFStruct.pixels_remaining--;
|
||||||
|
aPixelBlock[written++]
|
||||||
|
= ColormapIndexToPixel<PixelSize>(*--mGIFStruct.stackp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MOZ_UNLIKELY(mGIFStruct.stackp <= mGIFStruct.stack)) {
|
return MakeTuple(written, Maybe<WriteState>());
|
||||||
MOZ_ASSERT_UNREACHABLE("No decoded data but we didn't return early?");
|
|
||||||
return AsVariant(WriteState::FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yield a pixel at the appropriate index in the colormap.
|
|
||||||
mGIFStruct.pixels_remaining--;
|
|
||||||
return AsVariant(ColormapIndexToPixel<PixelSize>(*--mGIFStruct.stackp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expand the colormap from RGB to Packed ARGB as needed by Cairo.
|
/// Expand the colormap from RGB to Packed ARGB as needed by Cairo.
|
||||||
|
@ -1032,8 +1045,12 @@ nsGIFDecoder2::ReadLZWData(const char* aData, size_t aLength)
|
||||||
size_t bytesRead = 0;
|
size_t bytesRead = 0;
|
||||||
|
|
||||||
auto result = mGIFStruct.images_decoded == 0
|
auto result = mGIFStruct.images_decoded == 0
|
||||||
? mPipe.WritePixels<uint32_t>([&]{ return YieldPixel<uint32_t>(data, length, &bytesRead); })
|
? mPipe.WritePixelBlocks<uint32_t>([&](uint32_t* aPixelBlock, int32_t aBlockSize) {
|
||||||
: mPipe.WritePixels<uint8_t>([&]{ return YieldPixel<uint8_t>(data, length, &bytesRead); });
|
return YieldPixels<uint32_t>(data, length, &bytesRead, aPixelBlock, aBlockSize);
|
||||||
|
})
|
||||||
|
: mPipe.WritePixelBlocks<uint8_t>([&](uint8_t* aPixelBlock, int32_t aBlockSize) {
|
||||||
|
return YieldPixels<uint8_t>(data, length, &bytesRead, aPixelBlock, aBlockSize);
|
||||||
|
});
|
||||||
|
|
||||||
if (MOZ_UNLIKELY(bytesRead > length)) {
|
if (MOZ_UNLIKELY(bytesRead > length)) {
|
||||||
MOZ_ASSERT_UNREACHABLE("Overread?");
|
MOZ_ASSERT_UNREACHABLE("Overread?");
|
||||||
|
|
|
@ -65,8 +65,12 @@ private:
|
||||||
ColormapIndexToPixel(uint8_t aIndex);
|
ColormapIndexToPixel(uint8_t aIndex);
|
||||||
|
|
||||||
/// A generator function that performs LZW decompression and yields pixels.
|
/// A generator function that performs LZW decompression and yields pixels.
|
||||||
template <typename PixelSize> NextPixel<PixelSize>
|
template <typename PixelSize> Tuple<int32_t, Maybe<WriteState>>
|
||||||
YieldPixel(const uint8_t* aData, size_t aLength, size_t* aBytesReadOut);
|
YieldPixels(const uint8_t* aData,
|
||||||
|
size_t aLength,
|
||||||
|
size_t* aBytesReadOut,
|
||||||
|
PixelSize* aPixelBlock,
|
||||||
|
int32_t aBlockSize);
|
||||||
|
|
||||||
/// Checks if we have transparency, either because the header indicates that
|
/// Checks if we have transparency, either because the header indicates that
|
||||||
/// there's alpha, or because the frame rect doesn't cover the entire image.
|
/// there's alpha, or because the frame rect doesn't cover the entire image.
|
||||||
|
|
|
@ -305,12 +305,14 @@ imgTools::DecodeImageAsync(nsIInputStream* aInStr,
|
||||||
*/
|
*/
|
||||||
static nsresult
|
static nsresult
|
||||||
EncodeImageData(DataSourceSurface* aDataSurface,
|
EncodeImageData(DataSourceSurface* aDataSurface,
|
||||||
|
DataSourceSurface::ScopedMap& aMap,
|
||||||
const nsACString& aMimeType,
|
const nsACString& aMimeType,
|
||||||
const nsAString& aOutputOptions,
|
const nsAString& aOutputOptions,
|
||||||
nsIInputStream** aStream)
|
nsIInputStream** aStream)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8,
|
MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||||
"We're assuming B8G8R8A8");
|
aDataSurface->GetFormat() == SurfaceFormat::B8G8R8X8,
|
||||||
|
"We're assuming B8G8R8A8/X8");
|
||||||
|
|
||||||
// Get an image encoder for the media type
|
// Get an image encoder for the media type
|
||||||
nsAutoCString encoderCID(
|
nsAutoCString encoderCID(
|
||||||
|
@ -321,29 +323,37 @@ EncodeImageData(DataSourceSurface* aDataSurface,
|
||||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSourceSurface::MappedSurface map;
|
|
||||||
if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntSize size = aDataSurface->GetSize();
|
IntSize size = aDataSurface->GetSize();
|
||||||
uint32_t dataLength = map.mStride * size.height;
|
uint32_t dataLength = aMap.GetStride() * size.height;
|
||||||
|
|
||||||
// Encode the bitmap
|
// Encode the bitmap
|
||||||
nsresult rv = encoder->InitFromData(map.mData,
|
nsresult rv = encoder->InitFromData(aMap.GetData(),
|
||||||
dataLength,
|
dataLength,
|
||||||
size.width,
|
size.width,
|
||||||
size.height,
|
size.height,
|
||||||
map.mStride,
|
aMap.GetStride(),
|
||||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||||
aOutputOptions);
|
aOutputOptions);
|
||||||
aDataSurface->Unmap();
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
encoder.forget(aStream);
|
encoder.forget(aStream);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static nsresult
|
||||||
|
EncodeImageData(DataSourceSurface* aDataSurface,
|
||||||
|
const nsACString& aMimeType,
|
||||||
|
const nsAString& aOutputOptions,
|
||||||
|
nsIInputStream** aStream)
|
||||||
|
{
|
||||||
|
DataSourceSurface::ScopedMap map(aDataSurface, DataSourceSurface::READ);
|
||||||
|
if (!map.IsMapped()) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EncodeImageData(aDataSurface, map, aMimeType, aOutputOptions, aStream);
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
imgTools::EncodeImage(imgIContainer* aContainer,
|
imgTools::EncodeImage(imgIContainer* aContainer,
|
||||||
const nsACString& aMimeType,
|
const nsACString& aMimeType,
|
||||||
|
@ -358,7 +368,8 @@ imgTools::EncodeImage(imgIContainer* aContainer,
|
||||||
|
|
||||||
RefPtr<DataSourceSurface> dataSurface;
|
RefPtr<DataSourceSurface> dataSurface;
|
||||||
|
|
||||||
if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
|
if (frame->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||||
|
frame->GetFormat() == SurfaceFormat::B8G8R8X8) {
|
||||||
dataSurface = frame->GetDataSurface();
|
dataSurface = frame->GetDataSurface();
|
||||||
} else {
|
} else {
|
||||||
// Convert format to SurfaceFormat::B8G8R8A8
|
// Convert format to SurfaceFormat::B8G8R8A8
|
||||||
|
@ -407,22 +418,33 @@ imgTools::EncodeScaledImage(imgIContainer* aContainer,
|
||||||
imgIContainer::FLAG_SYNC_DECODE);
|
imgIContainer::FLAG_SYNC_DECODE);
|
||||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
// If the given surface is the right size/format, we can encode it directly.
|
||||||
|
if (scaledSize == frame->GetSize() &&
|
||||||
|
(frame->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||||
|
frame->GetFormat() == SurfaceFormat::B8G8R8X8)) {
|
||||||
|
RefPtr<DataSourceSurface> dataSurface = frame->GetDataSurface();
|
||||||
|
if (dataSurface) {
|
||||||
|
return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we need to scale it using a draw target.
|
||||||
RefPtr<DataSourceSurface> dataSurface =
|
RefPtr<DataSourceSurface> dataSurface =
|
||||||
Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
|
Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
|
||||||
if (NS_WARN_IF(!dataSurface)) {
|
if (NS_WARN_IF(!dataSurface)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSourceSurface::MappedSurface map;
|
DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
|
||||||
if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
|
if (!map.IsMapped()) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<DrawTarget> dt =
|
RefPtr<DrawTarget> dt =
|
||||||
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
Factory::CreateDrawTargetForData(BackendType::SKIA,
|
||||||
map.mData,
|
map.GetData(),
|
||||||
dataSurface->GetSize(),
|
dataSurface->GetSize(),
|
||||||
map.mStride,
|
map.GetStride(),
|
||||||
SurfaceFormat::B8G8R8A8);
|
SurfaceFormat::B8G8R8A8);
|
||||||
if (!dt) {
|
if (!dt) {
|
||||||
gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
|
gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
|
||||||
|
@ -436,9 +458,7 @@ imgTools::EncodeScaledImage(imgIContainer* aContainer,
|
||||||
DrawSurfaceOptions(),
|
DrawSurfaceOptions(),
|
||||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||||
|
|
||||||
dataSurface->Unmap();
|
return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream);
|
||||||
|
|
||||||
return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -492,16 +512,16 @@ imgTools::EncodeCroppedImage(imgIContainer* aContainer,
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSourceSurface::MappedSurface map;
|
DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
|
||||||
if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
|
if (!map.IsMapped()) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<DrawTarget> dt =
|
RefPtr<DrawTarget> dt =
|
||||||
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
Factory::CreateDrawTargetForData(BackendType::SKIA,
|
||||||
map.mData,
|
map.GetData(),
|
||||||
dataSurface->GetSize(),
|
dataSurface->GetSize(),
|
||||||
map.mStride,
|
map.GetStride(),
|
||||||
SurfaceFormat::B8G8R8A8);
|
SurfaceFormat::B8G8R8A8);
|
||||||
if (!dt) {
|
if (!dt) {
|
||||||
gfxWarning() <<
|
gfxWarning() <<
|
||||||
|
@ -512,9 +532,7 @@ imgTools::EncodeCroppedImage(imgIContainer* aContainer,
|
||||||
IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
|
IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
|
||||||
IntPoint(0, 0));
|
IntPoint(0, 0));
|
||||||
|
|
||||||
dataSurface->Unmap();
|
return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream);
|
||||||
|
|
||||||
return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
|
|
@ -201,6 +201,22 @@ TEST_F(ImageSurfacePipeIntegration, SurfacePipe)
|
||||||
CheckSurfacePipeMethodResults(&pipe, decoder);
|
CheckSurfacePipeMethodResults(&pipe, decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that WritePixelBlocks() gets passed through to the underlying pipeline.
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
WriteState result = pipe.WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
++count;
|
||||||
|
EXPECT_EQ(int32_t(100), aLength);
|
||||||
|
memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
|
||||||
|
return MakeTuple(int32_t(100), Maybe<WriteState>());
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::FINISHED, result);
|
||||||
|
EXPECT_EQ(100u, count);
|
||||||
|
CheckSurfacePipeMethodResults(&pipe, decoder);
|
||||||
|
}
|
||||||
|
|
||||||
// Test that WriteEmptyRow() gets passed through to the underlying pipeline.
|
// Test that WriteEmptyRow() gets passed through to the underlying pipeline.
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
@ -281,6 +297,22 @@ TEST_F(ImageSurfacePipeIntegration, PalettedSurfacePipe)
|
||||||
CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
|
CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that WritePixelBlocks() gets passed through to the underlying pipeline.
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
WriteState result = pipe.WritePixelBlocks<uint8_t>([&](uint8_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
++count;
|
||||||
|
EXPECT_EQ(int32_t(100), aLength);
|
||||||
|
memcpy(aBlockStart, buffer, 100 * sizeof(uint8_t));
|
||||||
|
return MakeTuple(int32_t(100), Maybe<WriteState>());
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::FINISHED, result);
|
||||||
|
EXPECT_EQ(100u, count);
|
||||||
|
CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
|
||||||
|
}
|
||||||
|
|
||||||
// Test that WriteEmptyRow() gets passed through to the underlying pipeline.
|
// Test that WriteEmptyRow() gets passed through to the underlying pipeline.
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
|
@ -602,6 +602,154 @@ TEST(ImageSurfaceSink, SurfaceSinkWriteUnsafeComputedRow)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ImageSurfaceSink, SurfaceSinkWritePixelBlocks)
|
||||||
|
{
|
||||||
|
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
|
||||||
|
// Create a green buffer the same size as one row of the surface (which is 100x100),
|
||||||
|
// containing 60 pixels of green in the middle and 20 transparent pixels on
|
||||||
|
// either side.
|
||||||
|
uint32_t buffer[100];
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
buffer[i] = 20 <= i && i < 80 ? BGRAColor::Green().AsPixel()
|
||||||
|
: BGRAColor::Transparent().AsPixel();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
WriteState result = aSink->WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
++count;
|
||||||
|
EXPECT_EQ(int32_t(100), aLength);
|
||||||
|
memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
|
||||||
|
return MakeTuple(int32_t(100), Maybe<WriteState>());
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::FINISHED, result);
|
||||||
|
EXPECT_EQ(100u, count);
|
||||||
|
|
||||||
|
AssertCorrectPipelineFinalState(aSink,
|
||||||
|
IntRect(0, 0, 100, 100),
|
||||||
|
IntRect(0, 0, 100, 100));
|
||||||
|
|
||||||
|
// Check that the generated image is correct.
|
||||||
|
CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));
|
||||||
|
|
||||||
|
// Attempt to write more and make sure that nothing gets written.
|
||||||
|
count = 0;
|
||||||
|
result = aSink->WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
count++;
|
||||||
|
for (int32_t i = 0; i < aLength; ++i) {
|
||||||
|
aBlockStart[i] = BGRAColor::Red().AsPixel();
|
||||||
|
}
|
||||||
|
return MakeTuple(aLength, Maybe<WriteState>());
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::FINISHED, result);
|
||||||
|
EXPECT_EQ(0u, count);
|
||||||
|
EXPECT_TRUE(aSink->IsSurfaceFinished());
|
||||||
|
|
||||||
|
// Check that the generated image is still correct.
|
||||||
|
CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImageSurfaceSink, SurfaceSinkWritePixelBlocksPartialRow)
|
||||||
|
{
|
||||||
|
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
|
||||||
|
// Create a green buffer the same size as one row of the surface (which is 100x100),
|
||||||
|
// containing 60 pixels of green in the middle and 20 transparent pixels on
|
||||||
|
// either side.
|
||||||
|
uint32_t buffer[100];
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
buffer[i] = 20 <= i && i < 80 ? BGRAColor::Green().AsPixel()
|
||||||
|
: BGRAColor::Transparent().AsPixel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the first 99 rows of our 100x100 surface and verify that even
|
||||||
|
// though our lambda will yield pixels forever, only one row is written per
|
||||||
|
// call to WritePixelsToRow().
|
||||||
|
for (int row = 0; row < 99; ++row) {
|
||||||
|
for (int32_t written = 0; written < 100; ) {
|
||||||
|
WriteState result = aSink->WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
// When we write the final block of pixels, it will request we start
|
||||||
|
// another row. We should abort at that point.
|
||||||
|
if (aLength == int32_t(100) && written == int32_t(100)) {
|
||||||
|
return MakeTuple(int32_t(0), Some(WriteState::NEED_MORE_DATA));
|
||||||
|
}
|
||||||
|
|
||||||
|
// It should always request enough data to fill the row. So it should
|
||||||
|
// request 100, 75, 50, and finally 25 pixels.
|
||||||
|
EXPECT_EQ(int32_t(100) - written, aLength);
|
||||||
|
|
||||||
|
// Only write one quarter of the pixels for the row.
|
||||||
|
memcpy(aBlockStart, &buffer[written], 25 * sizeof(uint32_t));
|
||||||
|
written += 25;
|
||||||
|
|
||||||
|
// We've written the last pixels remaining for the row.
|
||||||
|
if (written == int32_t(100)) {
|
||||||
|
return MakeTuple(int32_t(25), Maybe<WriteState>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've written another quarter of the row but not yet all of it.
|
||||||
|
return MakeTuple(int32_t(25), Some(WriteState::NEED_MORE_DATA));
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_FALSE(aSink->IsSurfaceFinished());
|
||||||
|
|
||||||
|
Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
|
||||||
|
EXPECT_TRUE(invalidRect.isSome());
|
||||||
|
EXPECT_EQ(IntRect(0, row, 100, 1), invalidRect->mInputSpaceRect);
|
||||||
|
EXPECT_EQ(IntRect(0, row, 100, 1), invalidRect->mOutputSpaceRect);
|
||||||
|
|
||||||
|
CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, row + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the final line, which should finish the surface.
|
||||||
|
uint32_t count = 0;
|
||||||
|
WriteState result = aSink->WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
++count;
|
||||||
|
EXPECT_EQ(int32_t(100), aLength);
|
||||||
|
memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
|
||||||
|
return MakeTuple(int32_t(100), Maybe<WriteState>());
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::FINISHED, result);
|
||||||
|
EXPECT_EQ(1u, count);
|
||||||
|
|
||||||
|
// Note that the final invalid rect we expect here is only the last row;
|
||||||
|
// that's because we called TakeInvalidRect() repeatedly in the loop above.
|
||||||
|
AssertCorrectPipelineFinalState(aSink,
|
||||||
|
IntRect(0, 99, 100, 1),
|
||||||
|
IntRect(0, 99, 100, 1));
|
||||||
|
|
||||||
|
// Check that the generated image is correct.
|
||||||
|
CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));
|
||||||
|
|
||||||
|
// Attempt to write more and make sure that nothing gets written.
|
||||||
|
count = 0;
|
||||||
|
result = aSink->WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
|
||||||
|
int32_t aLength) {
|
||||||
|
count++;
|
||||||
|
for (int32_t i = 0; i < aLength; ++i) {
|
||||||
|
aBlockStart[i] = BGRAColor::Red().AsPixel();
|
||||||
|
}
|
||||||
|
return MakeTuple(aLength, Maybe<WriteState>());
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(WriteState::FINISHED, result);
|
||||||
|
EXPECT_EQ(0u, count);
|
||||||
|
EXPECT_TRUE(aSink->IsSurfaceFinished());
|
||||||
|
|
||||||
|
// Check that the generated image is still correct.
|
||||||
|
CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ImageSurfaceSink, SurfaceSinkProgressivePasses)
|
TEST(ImageSurfaceSink, SurfaceSinkProgressivePasses)
|
||||||
{
|
{
|
||||||
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
|
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
|
||||||
|
|
|
@ -150,7 +150,7 @@ class Builder {
|
||||||
// A rooted reference to our value.
|
// A rooted reference to our value.
|
||||||
PersistentRooted<T> value;
|
PersistentRooted<T> value;
|
||||||
|
|
||||||
BuiltThing(JSContext* cx, Builder& owner_, T value_ = GCPolicy<T>::initial())
|
BuiltThing(JSContext* cx, Builder& owner_, T value_ = SafelyInitialized<T>())
|
||||||
: owner(owner_), value(cx, value_)
|
: owner(owner_), value(cx, value_)
|
||||||
{
|
{
|
||||||
owner.assertBuilt(value_);
|
owner.assertBuilt(value_);
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
//
|
//
|
||||||
// The GCPolicy provides at a minimum:
|
// The GCPolicy provides at a minimum:
|
||||||
//
|
//
|
||||||
// static T initial()
|
|
||||||
// - Construct and return an empty T.
|
|
||||||
//
|
|
||||||
// static void trace(JSTracer, T* tp, const char* name)
|
// static void trace(JSTracer, T* tp, const char* name)
|
||||||
// - Trace the edge |*tp|, calling the edge |name|. Containers like
|
// - Trace the edge |*tp|, calling the edge |name|. Containers like
|
||||||
// GCHashMap and GCHashSet use this method to trace their children.
|
// GCHashMap and GCHashSet use this method to trace their children.
|
||||||
|
@ -72,10 +69,6 @@ namespace JS {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct StructGCPolicy
|
struct StructGCPolicy
|
||||||
{
|
{
|
||||||
static T initial() {
|
|
||||||
return T();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void trace(JSTracer* trc, T* tp, const char* name) {
|
static void trace(JSTracer* trc, T* tp, const char* name) {
|
||||||
tp->trace(trc);
|
tp->trace(trc);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +95,6 @@ template <typename T> struct GCPolicy : public StructGCPolicy<T> {};
|
||||||
// This policy ignores any GC interaction, e.g. for non-GC types.
|
// This policy ignores any GC interaction, e.g. for non-GC types.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct IgnoreGCPolicy {
|
struct IgnoreGCPolicy {
|
||||||
static T initial() { return T(); }
|
|
||||||
static void trace(JSTracer* trc, T* t, const char* name) {}
|
static void trace(JSTracer* trc, T* t, const char* name) {}
|
||||||
static bool needsSweep(T* v) { return false; }
|
static bool needsSweep(T* v) { return false; }
|
||||||
static bool isValid(const T& v) { return true; }
|
static bool isValid(const T& v) { return true; }
|
||||||
|
@ -113,7 +105,6 @@ template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct GCPointerPolicy
|
struct GCPointerPolicy
|
||||||
{
|
{
|
||||||
static T initial() { return nullptr; }
|
|
||||||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||||
if (*vp)
|
if (*vp)
|
||||||
js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
|
js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
|
||||||
|
@ -140,7 +131,6 @@ template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct NonGCPointerPolicy
|
struct NonGCPointerPolicy
|
||||||
{
|
{
|
||||||
static T initial() { return nullptr; }
|
|
||||||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||||
if (*vp)
|
if (*vp)
|
||||||
(*vp)->trace(trc);
|
(*vp)->trace(trc);
|
||||||
|
@ -170,7 +160,6 @@ struct GCPolicy<JS::Heap<T>>
|
||||||
template <typename T, typename D>
|
template <typename T, typename D>
|
||||||
struct GCPolicy<mozilla::UniquePtr<T, D>>
|
struct GCPolicy<mozilla::UniquePtr<T, D>>
|
||||||
{
|
{
|
||||||
static mozilla::UniquePtr<T,D> initial() { return mozilla::UniquePtr<T,D>(); }
|
|
||||||
static void trace(JSTracer* trc, mozilla::UniquePtr<T,D>* tp, const char* name) {
|
static void trace(JSTracer* trc, mozilla::UniquePtr<T,D>* tp, const char* name) {
|
||||||
if (tp->get())
|
if (tp->get())
|
||||||
GCPolicy<T>::trace(trc, tp->get(), name);
|
GCPolicy<T>::trace(trc, tp->get(), name);
|
||||||
|
@ -192,7 +181,6 @@ struct GCPolicy<mozilla::UniquePtr<T, D>>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct GCPolicy<mozilla::Maybe<T>>
|
struct GCPolicy<mozilla::Maybe<T>>
|
||||||
{
|
{
|
||||||
static mozilla::Maybe<T> initial() { return mozilla::Maybe<T>(); }
|
|
||||||
static void trace(JSTracer* trc, mozilla::Maybe<T>* tp, const char* name) {
|
static void trace(JSTracer* trc, mozilla::Maybe<T>* tp, const char* name) {
|
||||||
if (tp->isSome())
|
if (tp->isSome())
|
||||||
GCPolicy<T>::trace(trc, tp->ptr(), name);
|
GCPolicy<T>::trace(trc, tp->ptr(), name);
|
||||||
|
|
|
@ -112,9 +112,6 @@ struct GCPolicy<mozilla::Variant<Ts...>>
|
||||||
{
|
{
|
||||||
using Impl = detail::GCVariantImplementation<Ts...>;
|
using Impl = detail::GCVariantImplementation<Ts...>;
|
||||||
|
|
||||||
// Variants do not provide initial(). They do not have a default initial
|
|
||||||
// value and one must be provided.
|
|
||||||
|
|
||||||
static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
|
static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
|
||||||
Impl::trace(trc, v, name);
|
Impl::trace(trc, v, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,6 @@ namespace JS {
|
||||||
template <>
|
template <>
|
||||||
struct GCPolicy<jsid>
|
struct GCPolicy<jsid>
|
||||||
{
|
{
|
||||||
static jsid initial() { return JSID_VOID; }
|
|
||||||
static void trace(JSTracer* trc, jsid* idp, const char* name) {
|
static void trace(JSTracer* trc, jsid* idp, const char* name) {
|
||||||
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
|
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,40 @@ template <typename T> class PersistentRooted;
|
||||||
JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next);
|
JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next);
|
||||||
JS_FRIEND_API(void) HeapStringPostBarrier(JSString** objp, JSString* prev, JSString* next);
|
JS_FRIEND_API(void) HeapStringPostBarrier(JSString** objp, JSString* prev, JSString* next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a safely-initialized |T|, suitable for use as a default value in
|
||||||
|
* situations requiring a safe but arbitrary |T| value.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
inline T
|
||||||
|
SafelyInitialized()
|
||||||
|
{
|
||||||
|
// This function wants to presume that |T()| -- which value-initializes a
|
||||||
|
// |T| per C++11 [expr.type.conv]p2 -- will produce a safely-initialized,
|
||||||
|
// safely-usable T that it can return.
|
||||||
|
|
||||||
|
#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_UNIX) && !defined(__clang__))
|
||||||
|
|
||||||
|
// That presumption holds for pointers, where value initialization produces
|
||||||
|
// a null pointer.
|
||||||
|
constexpr bool IsPointer = std::is_pointer<T>::value;
|
||||||
|
|
||||||
|
// For classes and unions we *assume* that if |T|'s default constructor is
|
||||||
|
// non-trivial it'll initialize correctly. (This is unideal, but C++
|
||||||
|
// doesn't offer a type trait indicating whether a class's constructor is
|
||||||
|
// user-defined, which better approximates our desired semantics.)
|
||||||
|
constexpr bool IsNonTriviallyDefaultConstructibleClassOrUnion =
|
||||||
|
(std::is_class<T>::value || std::is_union<T>::value) &&
|
||||||
|
!std::is_trivially_default_constructible<T>::value;
|
||||||
|
|
||||||
|
static_assert(IsPointer || IsNonTriviallyDefaultConstructibleClassOrUnion,
|
||||||
|
"T() must evaluate to a safely-initialized T");
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef JS_DEBUG
|
#ifdef JS_DEBUG
|
||||||
/**
|
/**
|
||||||
* For generational GC, assert that an object is in the tenured generation as
|
* For generational GC, assert that an object is in the tenured generation as
|
||||||
|
@ -247,7 +281,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>>
|
||||||
Heap() {
|
Heap() {
|
||||||
static_assert(sizeof(T) == sizeof(Heap<T>),
|
static_assert(sizeof(T) == sizeof(Heap<T>),
|
||||||
"Heap<T> must be binary compatible with T.");
|
"Heap<T> must be binary compatible with T.");
|
||||||
init(GCPolicy<T>::initial());
|
init(SafelyInitialized<T>());
|
||||||
}
|
}
|
||||||
explicit Heap(const T& p) { init(p); }
|
explicit Heap(const T& p) { init(p); }
|
||||||
|
|
||||||
|
@ -260,7 +294,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>>
|
||||||
explicit Heap(const Heap<T>& p) { init(p.ptr); }
|
explicit Heap(const Heap<T>& p) { init(p.ptr); }
|
||||||
|
|
||||||
~Heap() {
|
~Heap() {
|
||||||
post(ptr, GCPolicy<T>::initial());
|
post(ptr, SafelyInitialized<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_POINTER_CONSTREF_OPS(T);
|
DECLARE_POINTER_CONSTREF_OPS(T);
|
||||||
|
@ -291,7 +325,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>>
|
||||||
private:
|
private:
|
||||||
void init(const T& newPtr) {
|
void init(const T& newPtr) {
|
||||||
ptr = newPtr;
|
ptr = newPtr;
|
||||||
post(GCPolicy<T>::initial(), ptr);
|
post(SafelyInitialized<T>(), ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const T& newPtr) {
|
void set(const T& newPtr) {
|
||||||
|
@ -950,7 +984,7 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
|
||||||
|
|
||||||
template <typename RootingContext>
|
template <typename RootingContext>
|
||||||
explicit Rooted(const RootingContext& cx)
|
explicit Rooted(const RootingContext& cx)
|
||||||
: ptr(GCPolicy<T>::initial())
|
: ptr(SafelyInitialized<T>())
|
||||||
{
|
{
|
||||||
registerWithRootLists(rootLists(cx));
|
registerWithRootLists(rootLists(cx));
|
||||||
}
|
}
|
||||||
|
@ -1219,16 +1253,16 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
||||||
public:
|
public:
|
||||||
using ElementType = T;
|
using ElementType = T;
|
||||||
|
|
||||||
PersistentRooted() : ptr(GCPolicy<T>::initial()) {}
|
PersistentRooted() : ptr(SafelyInitialized<T>()) {}
|
||||||
|
|
||||||
explicit PersistentRooted(RootingContext* cx)
|
explicit PersistentRooted(RootingContext* cx)
|
||||||
: ptr(GCPolicy<T>::initial())
|
: ptr(SafelyInitialized<T>())
|
||||||
{
|
{
|
||||||
registerWithRootLists(cx);
|
registerWithRootLists(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit PersistentRooted(JSContext* cx)
|
explicit PersistentRooted(JSContext* cx)
|
||||||
: ptr(GCPolicy<T>::initial())
|
: ptr(SafelyInitialized<T>())
|
||||||
{
|
{
|
||||||
registerWithRootLists(RootingContext::get(cx));
|
registerWithRootLists(RootingContext::get(cx));
|
||||||
}
|
}
|
||||||
|
@ -1248,7 +1282,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit PersistentRooted(JSRuntime* rt)
|
explicit PersistentRooted(JSRuntime* rt)
|
||||||
: ptr(GCPolicy<T>::initial())
|
: ptr(SafelyInitialized<T>())
|
||||||
{
|
{
|
||||||
registerWithRootLists(rt);
|
registerWithRootLists(rt);
|
||||||
}
|
}
|
||||||
|
@ -1280,7 +1314,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(JSContext* cx) {
|
void init(JSContext* cx) {
|
||||||
init(cx, GCPolicy<T>::initial());
|
init(cx, SafelyInitialized<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
|
@ -1291,7 +1325,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
if (initialized()) {
|
if (initialized()) {
|
||||||
set(GCPolicy<T>::initial());
|
set(SafelyInitialized<T>());
|
||||||
ListBase::remove();
|
ListBase::remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1244,7 +1244,6 @@ JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const
|
||||||
template <>
|
template <>
|
||||||
struct GCPolicy<JS::Value>
|
struct GCPolicy<JS::Value>
|
||||||
{
|
{
|
||||||
static Value initial() { return UndefinedValue(); }
|
|
||||||
static void trace(JSTracer* trc, Value* v, const char* name) {
|
static void trace(JSTracer* trc, Value* v, const char* name) {
|
||||||
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
|
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3702,7 +3702,8 @@ CreateArrayPrototype(JSContext* cx, JSProtoKey key)
|
||||||
* arrays in JSON and script literals and allows setDenseArrayElement to
|
* arrays in JSON and script literals and allows setDenseArrayElement to
|
||||||
* be used without updating the indexed type set for such default arrays.
|
* be used without updating the indexed type set for such default arrays.
|
||||||
*/
|
*/
|
||||||
if (!JSObject::setNewGroupUnknown(cx, &ArrayObject::class_, arrayProto))
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
|
if (!JSObject::setNewGroupUnknown(cx, realm, &ArrayObject::class_, arrayProto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return arrayProto;
|
return arrayProto;
|
||||||
|
|
|
@ -376,7 +376,7 @@ MapIteratorObject::createResultPair(JSContext* cx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Rooted<TaggedProto> proto(cx, resultPairObj->taggedProto());
|
Rooted<TaggedProto> proto(cx, resultPairObj->taggedProto());
|
||||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto);
|
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, resultPairObj->getClass(), proto);
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
resultPairObj->setGroup(group);
|
resultPairObj->setGroup(group);
|
||||||
|
@ -1204,7 +1204,7 @@ SetIteratorObject::createResult(JSContext* cx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Rooted<TaggedProto> proto(cx, resultObj->taggedProto());
|
Rooted<TaggedProto> proto(cx, resultObj->taggedProto());
|
||||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultObj->getClass(), proto);
|
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, resultObj->getClass(), proto);
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
resultObj->setGroup(group);
|
resultObj->setGroup(group);
|
||||||
|
|
|
@ -350,8 +350,8 @@ function InnerModuleInstantiation(module, stack, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3
|
// Step 3
|
||||||
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
|
if (module.status !== MODULE_STATUS_UNINSTANTIATED)
|
||||||
"Bad module status in ModuleDeclarationInstantiation");
|
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
|
||||||
|
|
||||||
// Steps 4
|
// Steps 4
|
||||||
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
|
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
|
||||||
|
|
|
@ -2023,7 +2023,8 @@ CreateObjectPrototype(JSContext* cx, JSProtoKey key)
|
||||||
* to have unknown properties, to simplify handling of e.g. heterogenous
|
* to have unknown properties, to simplify handling of e.g. heterogenous
|
||||||
* objects in JSON and script literals.
|
* objects in JSON and script literals.
|
||||||
*/
|
*/
|
||||||
if (!JSObject::setNewGroupUnknown(cx, &PlainObject::class_, objectProto))
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
|
if (!JSObject::setNewGroupUnknown(cx, realm, &PlainObject::class_, objectProto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return objectProto;
|
return objectProto;
|
||||||
|
|
|
@ -48,7 +48,7 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Get the templateObject that defines the shape and type of the output object */
|
/* Get the templateObject that defines the shape and type of the output object */
|
||||||
JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
JSObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||||
if (!templateObject)
|
if (!templateObject)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1568,7 +1568,7 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
|
||||||
|
|
||||||
NativeObject* nproto = static_cast<NativeObject*>(proto);
|
NativeObject* nproto = static_cast<NativeObject*>(proto);
|
||||||
|
|
||||||
Shape* shape = cx->compartment()->regExps.getOptimizableRegExpPrototypeShape();
|
Shape* shape = cx->realm()->regExps.getOptimizableRegExpPrototypeShape();
|
||||||
if (shape == nproto->lastProperty())
|
if (shape == nproto->lastProperty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -1635,7 +1635,7 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
|
||||||
if (!has)
|
if (!has)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
cx->compartment()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
|
cx->realm()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1659,7 +1659,7 @@ js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
|
||||||
|
|
||||||
RegExpObject* rx = &obj->as<RegExpObject>();
|
RegExpObject* rx = &obj->as<RegExpObject>();
|
||||||
|
|
||||||
Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
|
Shape* shape = cx->realm()->regExps.getOptimizableRegExpInstanceShape();
|
||||||
if (shape == rx->lastProperty())
|
if (shape == rx->lastProperty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -1672,7 +1672,7 @@ js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
|
||||||
if (!RegExpObject::isInitialShape(rx))
|
if (!RegExpObject::isInitialShape(rx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
cx->compartment()->regExps.setOptimizableRegExpInstanceShape(rx->lastProperty());
|
cx->realm()->regExps.setOptimizableRegExpInstanceShape(rx->lastProperty());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4081,7 +4081,7 @@ BuildFlatMatchArray(JSContext* cx, HandleString str, HandleString pattern, int32
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the templateObject that defines the shape and type of the output object */
|
/* Get the templateObject that defines the shape and type of the output object */
|
||||||
JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
JSObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||||
if (!templateObject)
|
if (!templateObject)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -1271,7 +1271,7 @@ SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
// Either one or the other of the seed arguments must be non-zero;
|
// Either one or the other of the seed arguments must be non-zero;
|
||||||
// make this true no matter what value 'seed' has.
|
// make this true no matter what value 'seed' has.
|
||||||
cx->compartment()->savedStacks().setRNGState(seed, (seed + 1) * 33);
|
cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1279,7 +1279,7 @@ static bool
|
||||||
GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp)
|
GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
args.rval().setNumber(cx->compartment()->savedStacks().count());
|
args.rval().setNumber(cx->realm()->savedStacks().count());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1288,13 +1288,12 @@ ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
js::SavedStacks& savedStacks = cx->compartment()->savedStacks();
|
js::SavedStacks& savedStacks = cx->realm()->savedStacks();
|
||||||
if (savedStacks.initialized())
|
if (savedStacks.initialized())
|
||||||
savedStacks.clear();
|
savedStacks.clear();
|
||||||
|
|
||||||
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
|
for (ActivationIterator iter(cx); !iter.done(); ++iter)
|
||||||
iter->clearLiveSavedFrameCache();
|
iter->clearLiveSavedFrameCache();
|
||||||
}
|
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -409,7 +409,7 @@ template <class T>
|
||||||
class PreBarriered : public WriteBarrieredBase<T>
|
class PreBarriered : public WriteBarrieredBase<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PreBarriered() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
PreBarriered() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||||
/*
|
/*
|
||||||
* Allow implicit construction for use in generic contexts, such as
|
* Allow implicit construction for use in generic contexts, such as
|
||||||
* DebuggerWeakMap::markKeys.
|
* DebuggerWeakMap::markKeys.
|
||||||
|
@ -453,12 +453,12 @@ template <class T>
|
||||||
class GCPtr : public WriteBarrieredBase<T>
|
class GCPtr : public WriteBarrieredBase<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GCPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
GCPtr() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||||
explicit GCPtr(const T& v) : WriteBarrieredBase<T>(v) {
|
explicit GCPtr(const T& v) : WriteBarrieredBase<T>(v) {
|
||||||
this->post(JS::GCPolicy<T>::initial(), v);
|
this->post(JS::SafelyInitialized<T>(), v);
|
||||||
}
|
}
|
||||||
explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
|
explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
|
||||||
this->post(JS::GCPolicy<T>::initial(), v);
|
this->post(JS::SafelyInitialized<T>(), v);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
~GCPtr() {
|
~GCPtr() {
|
||||||
|
@ -471,7 +471,7 @@ class GCPtr : public WriteBarrieredBase<T>
|
||||||
//
|
//
|
||||||
// Note that when sweeping the wrapped pointer may already have been
|
// Note that when sweeping the wrapped pointer may already have been
|
||||||
// freed by this point.
|
// freed by this point.
|
||||||
MOZ_ASSERT(CurrentThreadIsGCSweeping() || this->value == JS::GCPolicy<T>::initial());
|
MOZ_ASSERT(CurrentThreadIsGCSweeping() || this->value == JS::SafelyInitialized<T>());
|
||||||
Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this), MemCheckKind::MakeNoAccess);
|
Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this), MemCheckKind::MakeNoAccess);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -479,7 +479,7 @@ class GCPtr : public WriteBarrieredBase<T>
|
||||||
void init(const T& v) {
|
void init(const T& v) {
|
||||||
CheckTargetIsNotGray(v);
|
CheckTargetIsNotGray(v);
|
||||||
this->value = v;
|
this->value = v;
|
||||||
this->post(JS::GCPolicy<T>::initial(), v);
|
this->post(JS::SafelyInitialized<T>(), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
|
DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
|
||||||
|
@ -529,11 +529,11 @@ template <class T>
|
||||||
class HeapPtr : public WriteBarrieredBase<T>
|
class HeapPtr : public WriteBarrieredBase<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HeapPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
HeapPtr() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||||
|
|
||||||
// Implicitly adding barriers is a reasonable default.
|
// Implicitly adding barriers is a reasonable default.
|
||||||
MOZ_IMPLICIT HeapPtr(const T& v) : WriteBarrieredBase<T>(v) {
|
MOZ_IMPLICIT HeapPtr(const T& v) : WriteBarrieredBase<T>(v) {
|
||||||
this->post(JS::GCPolicy<T>::initial(), this->value);
|
this->post(JS::SafelyInitialized<T>(), this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -543,18 +543,18 @@ class HeapPtr : public WriteBarrieredBase<T>
|
||||||
* simply omit the rvalue variant.
|
* simply omit the rvalue variant.
|
||||||
*/
|
*/
|
||||||
MOZ_IMPLICIT HeapPtr(const HeapPtr<T>& v) : WriteBarrieredBase<T>(v) {
|
MOZ_IMPLICIT HeapPtr(const HeapPtr<T>& v) : WriteBarrieredBase<T>(v) {
|
||||||
this->post(JS::GCPolicy<T>::initial(), this->value);
|
this->post(JS::SafelyInitialized<T>(), this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
~HeapPtr() {
|
~HeapPtr() {
|
||||||
this->pre();
|
this->pre();
|
||||||
this->post(this->value, JS::GCPolicy<T>::initial());
|
this->post(this->value, JS::SafelyInitialized<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(const T& v) {
|
void init(const T& v) {
|
||||||
CheckTargetIsNotGray(v);
|
CheckTargetIsNotGray(v);
|
||||||
this->value = v;
|
this->value = v;
|
||||||
this->post(JS::GCPolicy<T>::initial(), this->value);
|
this->post(JS::SafelyInitialized<T>(), this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
|
DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
|
||||||
|
@ -611,17 +611,17 @@ class ReadBarriered : public ReadBarrieredBase<T>,
|
||||||
using ReadBarrieredBase<T>::value;
|
using ReadBarrieredBase<T>::value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
ReadBarriered() : ReadBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||||
|
|
||||||
// It is okay to add barriers implicitly.
|
// It is okay to add barriers implicitly.
|
||||||
MOZ_IMPLICIT ReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {
|
MOZ_IMPLICIT ReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {
|
||||||
this->post(JS::GCPolicy<T>::initial(), v);
|
this->post(JS::SafelyInitialized<T>(), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The copy constructor creates a new weak edge but the wrapped pointer does
|
// The copy constructor creates a new weak edge but the wrapped pointer does
|
||||||
// not escape, so no read barrier is necessary.
|
// not escape, so no read barrier is necessary.
|
||||||
explicit ReadBarriered(const ReadBarriered& v) : ReadBarrieredBase<T>(v) {
|
explicit ReadBarriered(const ReadBarriered& v) : ReadBarrieredBase<T>(v) {
|
||||||
this->post(JS::GCPolicy<T>::initial(), v.unbarrieredGet());
|
this->post(JS::SafelyInitialized<T>(), v.unbarrieredGet());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move retains the lifetime status of the source edge, so does not fire
|
// Move retains the lifetime status of the source edge, so does not fire
|
||||||
|
@ -629,11 +629,11 @@ class ReadBarriered : public ReadBarrieredBase<T>,
|
||||||
ReadBarriered(ReadBarriered&& v)
|
ReadBarriered(ReadBarriered&& v)
|
||||||
: ReadBarrieredBase<T>(mozilla::Move(v))
|
: ReadBarrieredBase<T>(mozilla::Move(v))
|
||||||
{
|
{
|
||||||
this->post(JS::GCPolicy<T>::initial(), v.value);
|
this->post(JS::SafelyInitialized<T>(), v.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ReadBarriered() {
|
~ReadBarriered() {
|
||||||
this->post(this->value, JS::GCPolicy<T>::initial());
|
this->post(this->value, JS::SafelyInitialized<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadBarriered& operator=(const ReadBarriered& v) {
|
ReadBarriered& operator=(const ReadBarriered& v) {
|
||||||
|
|
|
@ -2577,7 +2577,7 @@ GCRuntime::sweepZoneAfterCompacting(Zone* zone)
|
||||||
jitZone->sweep();
|
jitZone->sweep();
|
||||||
|
|
||||||
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
|
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
|
||||||
r->objectGroups.sweep();
|
r->sweepObjectGroups();
|
||||||
r->sweepRegExps();
|
r->sweepRegExps();
|
||||||
r->sweepSavedStacks();
|
r->sweepSavedStacks();
|
||||||
r->sweepVarNames();
|
r->sweepVarNames();
|
||||||
|
@ -4253,8 +4253,9 @@ GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOu
|
||||||
|
|
||||||
for (RealmsIter r(rt, WithAtoms); !r.done(); r.next()) {
|
for (RealmsIter r(rt, WithAtoms); !r.done(); r.next()) {
|
||||||
r->unmark();
|
r->unmark();
|
||||||
r->scheduledForDestruction = false;
|
JSCompartment* comp = JS::GetCompartmentForRealm(r);
|
||||||
r->maybeAlive = r->shouldTraceGlobal() || !r->zone()->isGCScheduled();
|
comp->scheduledForDestruction = false;
|
||||||
|
comp->maybeAlive = r->shouldTraceGlobal() || !r->zone()->isGCScheduled();
|
||||||
if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode))
|
if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode))
|
||||||
r->zone()->setPreservingCode(true);
|
r->zone()->setPreservingCode(true);
|
||||||
}
|
}
|
||||||
|
@ -5424,8 +5425,8 @@ static void
|
||||||
SweepObjectGroups(GCParallelTask* task)
|
SweepObjectGroups(GCParallelTask* task)
|
||||||
{
|
{
|
||||||
JSRuntime* runtime = task->runtime();
|
JSRuntime* runtime = task->runtime();
|
||||||
for (SweepGroupCompartmentsIter c(runtime); !c.done(); c.next())
|
for (SweepGroupRealmsIter r(runtime); !r.done(); r.next())
|
||||||
c->objectGroups.sweep();
|
r->sweepObjectGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -5532,8 +5533,8 @@ GCRuntime::sweepDebuggerOnMainThread(FreeOp* fop)
|
||||||
// table.
|
// table.
|
||||||
{
|
{
|
||||||
gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::SWEEP_MISC);
|
gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::SWEEP_MISC);
|
||||||
for (SweepGroupCompartmentsIter c(rt); !c.done(); c.next())
|
for (SweepGroupRealmsIter r(rt); !r.done(); r.next())
|
||||||
c->sweepDebugEnvironments();
|
r->sweepDebugEnvironments();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sweep breakpoints. This is done here to be with the other debug sweeping,
|
// Sweep breakpoints. This is done here to be with the other debug sweeping,
|
||||||
|
@ -7953,16 +7954,16 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedJSDeletePtr<Realm> compartment(cx->new_<Realm>(zone, options));
|
ScopedJSDeletePtr<Realm> realm(cx->new_<Realm>(zone, options));
|
||||||
if (!compartment || !compartment->init(cx))
|
if (!realm || !realm->init(cx))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Set up the principals.
|
// Set up the principals.
|
||||||
JS_SetCompartmentPrincipals(compartment, principals);
|
JS_SetCompartmentPrincipals(JS::GetCompartmentForRealm(realm), principals);
|
||||||
|
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
|
|
||||||
if (!zone->compartments().append(compartment.get())) {
|
if (!zone->compartments().append(JS::GetCompartmentForRealm(realm.get()))) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -7982,7 +7983,7 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneHolder.forget();
|
zoneHolder.forget();
|
||||||
return compartment.forget();
|
return JS::GetCompartmentForRealm(realm.forget());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -8482,12 +8483,12 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) {
|
for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) {
|
||||||
r->objectGroups.checkTablesAfterMovingGC();
|
r->checkObjectGroupTablesAfterMovingGC();
|
||||||
r->dtoaCache.checkCacheAfterMovingGC();
|
r->dtoaCache.checkCacheAfterMovingGC();
|
||||||
JS::GetCompartmentForRealm(r)->checkWrapperMapAfterMovingGC();
|
JS::GetCompartmentForRealm(r)->checkWrapperMapAfterMovingGC();
|
||||||
r->checkScriptMapsAfterMovingGC();
|
r->checkScriptMapsAfterMovingGC();
|
||||||
if (r->debugEnvs)
|
if (r->debugEnvs())
|
||||||
r->debugEnvs->checkHashTablesAfterMovingGC();
|
r->debugEnvs()->checkHashTablesAfterMovingGC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,7 +23,7 @@ template <typename T>
|
||||||
class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
|
class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||||
MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
|
MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
|
||||||
explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
|
explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
|
||||||
UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
|
UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
|
||||||
|
@ -42,7 +42,7 @@ class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
|
||||||
|
|
||||||
const T get() const {
|
const T get() const {
|
||||||
if (!InternalBarrierMethods<T>::isMarkable(this->value))
|
if (!InternalBarrierMethods<T>::isMarkable(this->value))
|
||||||
return JS::GCPolicy<T>::initial();
|
return JS::SafelyInitialized<T>();
|
||||||
this->read();
|
this->read();
|
||||||
return this->value;
|
return this->value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,6 @@ namespace js {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct InternalGCPointerPolicy {
|
struct InternalGCPointerPolicy {
|
||||||
using Type = typename mozilla::RemovePointer<T>::Type;
|
using Type = typename mozilla::RemovePointer<T>::Type;
|
||||||
static T initial() { return nullptr; }
|
|
||||||
static void preBarrier(T v) {
|
static void preBarrier(T v) {
|
||||||
if (v)
|
if (v)
|
||||||
Type::writeBarrierPre(v);
|
Type::writeBarrierPre(v);
|
||||||
|
|
|
@ -89,7 +89,7 @@ class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
|
||||||
using ElementType = T;
|
using ElementType = T;
|
||||||
|
|
||||||
template <typename CX>
|
template <typename CX>
|
||||||
explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
|
explicit FakeRooted(CX* cx) : ptr(JS::SafelyInitialized<T>()) {}
|
||||||
|
|
||||||
template <typename CX>
|
template <typename CX>
|
||||||
FakeRooted(CX* cx, T initial) : ptr(initial) {}
|
FakeRooted(CX* cx, T initial) : ptr(initial) {}
|
||||||
|
|
|
@ -2,7 +2,7 @@ if (!('oomTest' in this))
|
||||||
quit();
|
quit();
|
||||||
|
|
||||||
oomTest(() => {
|
oomTest(() => {
|
||||||
var g = newGlobal();
|
var g = newGlobal({sameZoneAs: this});
|
||||||
g.eval("\
|
g.eval("\
|
||||||
function f(){}; \
|
function f(){}; \
|
||||||
getLcovInfo(); \
|
getLcovInfo(); \
|
||||||
|
|
|
@ -10,5 +10,5 @@ for (var i = 0; i < 9; ++i) {
|
||||||
}
|
}
|
||||||
// jsfunfuzz-generated
|
// jsfunfuzz-generated
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
newGlobal();
|
newGlobal({sameZoneAs: this});
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,5 +8,5 @@ dbg.onNewGlobalObject = function(global) {
|
||||||
dbg.memory.takeCensus({});
|
dbg.memory.takeCensus({});
|
||||||
};
|
};
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
newGlobal({})
|
newGlobal({sameZoneAs: this})
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,5 +9,5 @@ fullcompartmentchecks(true);
|
||||||
var dbg = new Debugger;
|
var dbg = new Debugger;
|
||||||
dbg.onNewGlobalObject = function() {};
|
dbg.onNewGlobalObject = function() {};
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
newGlobal();
|
newGlobal({sameZoneAs: this});
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,6 +12,6 @@ for (var i = 0; i < 9; ++ i) {
|
||||||
dbg.onNewGlobalObject = ERROR;
|
dbg.onNewGlobalObject = ERROR;
|
||||||
}
|
}
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
newGlobal();
|
newGlobal({sameZoneAs: this});
|
||||||
})
|
})
|
||||||
`);
|
`);
|
||||||
|
|
|
@ -10,6 +10,6 @@ dbg.onNewScript = function (s) {
|
||||||
}
|
}
|
||||||
log = "";
|
log = "";
|
||||||
oomTest(() => {
|
oomTest(() => {
|
||||||
var static = newGlobal();
|
var static = newGlobal({sameZoneAs: this});
|
||||||
g.eval("(function() {})()");
|
g.eval("(function() {})()");
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,5 @@ dbg.onNewGlobalObject = function(global) {
|
||||||
get.seen = true;
|
get.seen = true;
|
||||||
};
|
};
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
newGlobal({
|
newGlobal({sameZoneAs: this})
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ loadFile(`
|
||||||
var global = this;
|
var global = this;
|
||||||
var p = new Proxy(o, {
|
var p = new Proxy(o, {
|
||||||
"deleteProperty": function (await , key) {
|
"deleteProperty": function (await , key) {
|
||||||
var g = newGlobal();
|
var g = newGlobal({sameZoneAs: this});
|
||||||
g.parent = global;
|
g.parent = global;
|
||||||
g.eval("var dbg = new Debugger(parent); dbg.onEnterFrame = function(frame) {};");
|
g.eval("var dbg = new Debugger(parent); dbg.onEnterFrame = function(frame) {};");
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,6 @@ function ERROR(msg) {
|
||||||
var dbg = new Debugger;
|
var dbg = new Debugger;
|
||||||
dbg.onNewGlobalObject = ERROR;
|
dbg.onNewGlobalObject = ERROR;
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
newGlobal();
|
newGlobal({sameZoneAs: this});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
if (!('oomTest' in this))
|
if (!('oomTest' in this))
|
||||||
quit();
|
quit();
|
||||||
|
|
||||||
var source = `
|
function x() {
|
||||||
var global = newGlobal();
|
var global = newGlobal({sameZoneAs: this});
|
||||||
global.eval('function f() { debugger; }');
|
global.eval('function f() { debugger; }');
|
||||||
var debug = new Debugger(global);
|
var debug = new Debugger(global);
|
||||||
var foo;
|
var foo;
|
||||||
|
@ -13,8 +13,6 @@ var source = `
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
global.eval('f(0)');
|
global.eval('f(0)');
|
||||||
`;
|
|
||||||
function test() {
|
|
||||||
oomTest(new Function(source), false);
|
|
||||||
}
|
}
|
||||||
test();
|
|
||||||
|
oomTest(x, false);
|
||||||
|
|
|
@ -4,6 +4,6 @@ if (!('oomTest' in this))
|
||||||
quit();
|
quit();
|
||||||
|
|
||||||
oomTest(() => {
|
oomTest(() => {
|
||||||
var g = newGlobal();
|
var g = newGlobal({sameZoneAs: this});
|
||||||
g.eval("(function() {})()");
|
g.eval("(function() {})()");
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ if (!('oomTest' in this))
|
||||||
quit();
|
quit();
|
||||||
|
|
||||||
oomTest(() => {
|
oomTest(() => {
|
||||||
let global = newGlobal();
|
let global = newGlobal({sameZoneAs: this});
|
||||||
Debugger(global).onDebuggerStatement = function (frame) {
|
Debugger(global).onDebuggerStatement = function (frame) {
|
||||||
frame.eval("f")
|
frame.eval("f")
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ if (!('oomTest' in this))
|
||||||
|
|
||||||
var x = ``.split();
|
var x = ``.split();
|
||||||
oomTest(function() {
|
oomTest(function() {
|
||||||
var lfGlobal = newGlobal();
|
var lfGlobal = newGlobal({sameZoneAs: this});
|
||||||
for (lfLocal in this) {
|
for (lfLocal in this) {
|
||||||
if (!(lfLocal in lfGlobal)) {
|
if (!(lfLocal in lfGlobal)) {
|
||||||
lfGlobal[lfLocal] = this[lfLocal];
|
lfGlobal[lfLocal] = this[lfLocal];
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
// |jit-test| error: Error
|
||||||
|
|
||||||
|
var g = newGlobal();
|
||||||
|
g.eval(`
|
||||||
|
setModuleResolveHook(function(module, specifier) { return module; });
|
||||||
|
`);
|
||||||
|
let m = parseModule(`
|
||||||
|
import {} from './foo.js';
|
||||||
|
`);
|
||||||
|
m.declarationInstantiation();
|
|
@ -0,0 +1,11 @@
|
||||||
|
// |jit-test| error: InternalError
|
||||||
|
|
||||||
|
let m = parseModule(`
|
||||||
|
let c = parseModule(\`
|
||||||
|
import "a";
|
||||||
|
\`);
|
||||||
|
c.declarationInstantiation();
|
||||||
|
`);
|
||||||
|
setModuleResolveHook(function(module, specifier) { return m; });
|
||||||
|
m.declarationInstantiation();
|
||||||
|
m.evaluation();
|
|
@ -350,32 +350,32 @@ if (typeof WebAssembly.Global === "function") {
|
||||||
const Global = WebAssembly.Global;
|
const Global = WebAssembly.Global;
|
||||||
|
|
||||||
// These types should work:
|
// These types should work:
|
||||||
assertEq(new Global({type: "i32"}) instanceof Global, true);
|
assertEq(new Global({value: "i32"}) instanceof Global, true);
|
||||||
assertEq(new Global({type: "f32"}) instanceof Global, true);
|
assertEq(new Global({value: "f32"}) instanceof Global, true);
|
||||||
assertEq(new Global({type: "f64"}) instanceof Global, true);
|
assertEq(new Global({value: "f64"}) instanceof Global, true);
|
||||||
|
|
||||||
// These types should not work:
|
// These types should not work:
|
||||||
assertErrorMessage(() => new Global({type: "i64"}), TypeError, /bad type for a WebAssembly.Global/);
|
assertErrorMessage(() => new Global({value: "i64"}), TypeError, /bad type for a WebAssembly.Global/);
|
||||||
assertErrorMessage(() => new Global({}), TypeError, /bad type for a WebAssembly.Global/);
|
assertErrorMessage(() => new Global({}), TypeError, /bad type for a WebAssembly.Global/);
|
||||||
assertErrorMessage(() => new Global({type: "fnord"}), TypeError, /bad type for a WebAssembly.Global/);
|
assertErrorMessage(() => new Global({value: "fnord"}), TypeError, /bad type for a WebAssembly.Global/);
|
||||||
assertErrorMessage(() => new Global(), TypeError, /Global requires more than 0 arguments/);
|
assertErrorMessage(() => new Global(), TypeError, /Global requires more than 0 arguments/);
|
||||||
|
|
||||||
// Coercion of init value; ".value" accessor
|
// Coercion of init value; ".value" accessor
|
||||||
assertEq((new Global({type: "i32"}, 3.14)).value, 3);
|
assertEq((new Global({value: "i32"}, 3.14)).value, 3);
|
||||||
assertEq((new Global({type: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
|
assertEq((new Global({value: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
|
||||||
assertEq((new Global({type: "f64"}, "3.25")).value, 3.25);
|
assertEq((new Global({value: "f64"}, "3.25")).value, 3.25);
|
||||||
|
|
||||||
// Nothing special about NaN, it coerces just fine
|
// Nothing special about NaN, it coerces just fine
|
||||||
assertEq((new Global({type: "i32"}, NaN)).value, 0);
|
assertEq((new Global({value: "i32"}, NaN)).value, 0);
|
||||||
|
|
||||||
// The default init value is zero.
|
// The default init value is zero.
|
||||||
assertEq((new Global({type: "i32"})).value, 0);
|
assertEq((new Global({value: "i32"})).value, 0);
|
||||||
assertEq((new Global({type: "f32"})).value, 0);
|
assertEq((new Global({value: "f32"})).value, 0);
|
||||||
assertEq((new Global({type: "f64"})).value, 0);
|
assertEq((new Global({value: "f64"})).value, 0);
|
||||||
|
|
||||||
{
|
{
|
||||||
// "value" is enumerable
|
// "value" is enumerable
|
||||||
let x = new Global({type: "i32"});
|
let x = new Global({value: "i32"});
|
||||||
let s = "";
|
let s = "";
|
||||||
for ( let i in x )
|
for ( let i in x )
|
||||||
s = s + i + ",";
|
s = s + i + ",";
|
||||||
|
@ -386,20 +386,20 @@ if (typeof WebAssembly.Global === "function") {
|
||||||
assertEq("value" in Global.prototype, true);
|
assertEq("value" in Global.prototype, true);
|
||||||
|
|
||||||
// Can't set the value of an immutable global
|
// Can't set the value of an immutable global
|
||||||
assertErrorMessage(() => (new Global({type: "i32"})).value = 10,
|
assertErrorMessage(() => (new Global({value: "i32"})).value = 10,
|
||||||
TypeError,
|
TypeError,
|
||||||
/can't set value of immutable global/);
|
/can't set value of immutable global/);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Can set the value of a mutable global
|
// Can set the value of a mutable global
|
||||||
let g = new Global({type: "i32", mutable: true}, 37);
|
let g = new Global({value: "i32", mutable: true}, 37);
|
||||||
g.value = 10;
|
g.value = 10;
|
||||||
assertEq(g.value, 10);
|
assertEq(g.value, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Misc internal conversions
|
// Misc internal conversions
|
||||||
let g = new Global({type: "i32"}, 42);
|
let g = new Global({value: "i32"}, 42);
|
||||||
|
|
||||||
// valueOf
|
// valueOf
|
||||||
assertEq(g - 5, 37);
|
assertEq(g - 5, 37);
|
||||||
|
@ -513,7 +513,7 @@ if (typeof WebAssembly.Global === "function") {
|
||||||
(import "m" "g" (global i32)))`));
|
(import "m" "g" (global i32)))`));
|
||||||
|
|
||||||
// Mutable Global matched to immutable import
|
// Mutable Global matched to immutable import
|
||||||
let gm = new Global({type: "i32", mutable: true}, 42);
|
let gm = new Global({value: "i32", mutable: true}, 42);
|
||||||
assertErrorMessage(() => new Instance(m1, {m: {g: gm}}),
|
assertErrorMessage(() => new Instance(m1, {m: {g: gm}}),
|
||||||
LinkError,
|
LinkError,
|
||||||
mutErr);
|
mutErr);
|
||||||
|
@ -522,7 +522,7 @@ if (typeof WebAssembly.Global === "function") {
|
||||||
(import "m" "g" (global (mut i32))))`));
|
(import "m" "g" (global (mut i32))))`));
|
||||||
|
|
||||||
// Immutable Global matched to mutable import
|
// Immutable Global matched to mutable import
|
||||||
let gi = new Global({type: "i32", mutable: false}, 42);
|
let gi = new Global({value: "i32", mutable: false}, 42);
|
||||||
assertErrorMessage(() => new Instance(m2, {m: {g: gi}}),
|
assertErrorMessage(() => new Instance(m2, {m: {g: gi}}),
|
||||||
LinkError,
|
LinkError,
|
||||||
mutErr);
|
mutErr);
|
||||||
|
|
|
@ -6,7 +6,7 @@ if (typeof oomTest !== 'function' || !wasmIsSupported()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function foo() {
|
function foo() {
|
||||||
var g = newGlobal();
|
var g = newGlobal({sameZoneAs: this});
|
||||||
g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
|
g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
|
||||||
}
|
}
|
||||||
oomTest(foo);
|
oomTest(foo);
|
||||||
|
|
|
@ -83,8 +83,8 @@ BaselineFrame::trace(JSTracer* trc, const JSJitFrameIter& frameIterator)
|
||||||
TraceLocals(this, trc, 0, nlivefixed);
|
TraceLocals(this, trc, 0, nlivefixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script->compartment()->debugEnvs)
|
if (auto* debugEnvs = script->realm()->debugEnvs())
|
||||||
script->compartment()->debugEnvs->traceLiveFrame(trc, this);
|
debugEnvs->traceLiveFrame(trc, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -4406,7 +4406,7 @@ CallIRGenerator::tryAttachStringSplit()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the object group to use for this location.
|
// Get the object group to use for this location.
|
||||||
RootedObjectGroup group(cx_, ObjectGroupCompartment::getStringSplitStringGroup(cx_));
|
RootedObjectGroup group(cx_, ObjectGroupRealm::getStringSplitStringGroup(cx_));
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -1930,7 +1930,7 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx)
|
||||||
maybeTemp4 = regs.takeAny();
|
maybeTemp4 = regs.takeAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
ArrayObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||||
if (!templateObject)
|
if (!templateObject)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -2543,7 +2543,7 @@ CodeGenerator::visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
|
||||||
masm.loadJSContext(temp);
|
masm.loadJSContext(temp);
|
||||||
masm.loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
|
masm.loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
|
||||||
size_t offset = Realm::offsetOfRegExps() +
|
size_t offset = Realm::offsetOfRegExps() +
|
||||||
RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape();
|
RegExpRealm::offsetOfOptimizableRegExpPrototypeShape();
|
||||||
masm.loadPtr(Address(temp, offset), temp);
|
masm.loadPtr(Address(temp, offset), temp);
|
||||||
|
|
||||||
masm.branchTestObjShapeUnsafe(Assembler::NotEqual, object, temp, ool->entry());
|
masm.branchTestObjShapeUnsafe(Assembler::NotEqual, object, temp, ool->entry());
|
||||||
|
@ -2603,7 +2603,7 @@ CodeGenerator::visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
|
||||||
masm.loadJSContext(temp);
|
masm.loadJSContext(temp);
|
||||||
masm.loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
|
masm.loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
|
||||||
size_t offset = Realm::offsetOfRegExps() +
|
size_t offset = Realm::offsetOfRegExps() +
|
||||||
RegExpCompartment::offsetOfOptimizableRegExpInstanceShape();
|
RegExpRealm::offsetOfOptimizableRegExpInstanceShape();
|
||||||
masm.loadPtr(Address(temp, offset), temp);
|
masm.loadPtr(Address(temp, offset), temp);
|
||||||
|
|
||||||
masm.branchTestObjShapeUnsafe(Assembler::NotEqual, object, temp, ool->entry());
|
masm.branchTestObjShapeUnsafe(Assembler::NotEqual, object, temp, ool->entry());
|
||||||
|
|
|
@ -1770,7 +1770,7 @@ IonBuilder::inlineStringSplitString(CallInfo& callInfo)
|
||||||
return resultConstStringSplit;
|
return resultConstStringSplit;
|
||||||
|
|
||||||
JSContext* cx = TlsContext.get();
|
JSContext* cx = TlsContext.get();
|
||||||
ObjectGroup* group = ObjectGroupCompartment::getStringSplitStringGroup(cx);
|
ObjectGroup* group = ObjectGroupRealm::getStringSplitStringGroup(cx);
|
||||||
if (!group)
|
if (!group)
|
||||||
return InliningStatus_NotInlined;
|
return InliningStatus_NotInlined;
|
||||||
AutoSweepObjectGroup sweep(group);
|
AutoSweepObjectGroup sweep(group);
|
||||||
|
|
|
@ -1129,7 +1129,7 @@ RStringSplit::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||||
{
|
{
|
||||||
RootedString str(cx, iter.read().toString());
|
RootedString str(cx, iter.read().toString());
|
||||||
RootedString sep(cx, iter.read().toString());
|
RootedString sep(cx, iter.read().toString());
|
||||||
RootedObjectGroup group(cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
|
RootedObjectGroup group(cx, ObjectGroupRealm::getStringSplitStringGroup(cx));
|
||||||
if (!group) {
|
if (!group) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,6 +371,7 @@ MSG_DEF(JSMSG_WASM_BAD_FIT, 2, JSEXN_WASMLINKERROR, "{0} segment does
|
||||||
MSG_DEF(JSMSG_WASM_BAD_I64_LINK, 0, JSEXN_WASMLINKERROR, "cannot pass i64 to or from JS")
|
MSG_DEF(JSMSG_WASM_BAD_I64_LINK, 0, JSEXN_WASMLINKERROR, "cannot pass i64 to or from JS")
|
||||||
MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK, 0, JSEXN_WASMLINKERROR, "shared memory is disabled")
|
MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK, 0, JSEXN_WASMLINKERROR, "shared memory is disabled")
|
||||||
MSG_DEF(JSMSG_WASM_BAD_MUT_LINK, 0, JSEXN_WASMLINKERROR, "imported global mutability mismatch")
|
MSG_DEF(JSMSG_WASM_BAD_MUT_LINK, 0, JSEXN_WASMLINKERROR, "imported global mutability mismatch")
|
||||||
|
MSG_DEF(JSMSG_WASM_BAD_TYPE_LINK, 0, JSEXN_WASMLINKERROR, "imported global type mismatch")
|
||||||
MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL, 0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
|
MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL, 0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
|
||||||
MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG, 0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
|
MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG, 0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
|
||||||
MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
|
MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
|
||||||
|
|
|
@ -682,7 +682,7 @@ JS::EnterRealm(JSContext* cx, JSObject* target)
|
||||||
|
|
||||||
Realm* oldRealm = cx->realm();
|
Realm* oldRealm = cx->realm();
|
||||||
cx->enterRealmOf(target);
|
cx->enterRealmOf(target);
|
||||||
return JS::GetRealmForCompartment(oldRealm);
|
return JS::GetCompartmentForRealm(oldRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
|
@ -7796,11 +7796,11 @@ JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp,
|
||||||
{
|
{
|
||||||
AssertHeapIsIdle();
|
AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
JSCompartment* compartment = cx->compartment();
|
Realm* realm = cx->realm();
|
||||||
Rooted<SavedFrame*> frame(cx);
|
Rooted<SavedFrame*> frame(cx);
|
||||||
if (!compartment->savedStacks().saveCurrentStack(cx, &frame, mozilla::Move(capture)))
|
if (!realm->savedStacks().saveCurrentStack(cx, &frame, mozilla::Move(capture)))
|
||||||
return false;
|
return false;
|
||||||
stackp.set(frame.get());
|
stackp.set(frame.get());
|
||||||
return true;
|
return true;
|
||||||
|
@ -7813,13 +7813,12 @@ JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
|
||||||
{
|
{
|
||||||
AssertHeapIsIdle();
|
AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
|
js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
|
||||||
JSCompartment* compartment = cx->compartment();
|
Realm* realm = cx->realm();
|
||||||
Rooted<SavedFrame*> frame(cx);
|
Rooted<SavedFrame*> frame(cx);
|
||||||
if (!compartment->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause,
|
if (!realm->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause, &frame, maxFrameCount))
|
||||||
&frame, maxFrameCount))
|
|
||||||
return false;
|
return false;
|
||||||
stackp.set(frame.get());
|
stackp.set(frame.get());
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -447,7 +447,7 @@ ErrorCopier::~ErrorCopier()
|
||||||
|
|
||||||
// The provenance of Debugger.DebuggeeWouldRun is the topmost locking
|
// The provenance of Debugger.DebuggeeWouldRun is the topmost locking
|
||||||
// debugger compartment; it should not be copied around.
|
// debugger compartment; it should not be copied around.
|
||||||
if (ar->origin() != cx->compartment() &&
|
if (JS::GetCompartmentForRealm(ar->origin()) != cx->compartment() &&
|
||||||
cx->isExceptionPending() &&
|
cx->isExceptionPending() &&
|
||||||
!cx->isThrowingDebuggeeWouldRun())
|
!cx->isThrowingDebuggeeWouldRun())
|
||||||
{
|
{
|
||||||
|
|
|
@ -172,6 +172,15 @@ enum JSShellExitCode {
|
||||||
EXITCODE_TIMEOUT = 6
|
EXITCODE_TIMEOUT = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Define use of application-specific slots on the shell's global object.
|
||||||
|
enum GlobalAppSlot
|
||||||
|
{
|
||||||
|
GlobalAppSlotModuleResolveHook,
|
||||||
|
GlobalAppSlotCount
|
||||||
|
};
|
||||||
|
static_assert(GlobalAppSlotCount <= JSCLASS_GLOBAL_APPLICATION_SLOTS,
|
||||||
|
"Too many applications slots defined for shell global");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: This limit should match the stack limit set by the browser in
|
* Note: This limit should match the stack limit set by the browser in
|
||||||
* js/xpconnect/src/XPCJSContext.cpp
|
* js/xpconnect/src/XPCJSContext.cpp
|
||||||
|
@ -610,8 +619,7 @@ ShellContext::ShellContext(JSContext* cx)
|
||||||
readLineBufPos(0),
|
readLineBufPos(0),
|
||||||
errFilePtr(nullptr),
|
errFilePtr(nullptr),
|
||||||
outFilePtr(nullptr),
|
outFilePtr(nullptr),
|
||||||
offThreadMonitor(mutexid::ShellOffThreadState),
|
offThreadMonitor(mutexid::ShellOffThreadState)
|
||||||
moduleResolveHook(cx)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
ShellContext::~ShellContext()
|
ShellContext::~ShellContext()
|
||||||
|
@ -4279,8 +4287,8 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellContext* sc = GetShellContext(cx);
|
Handle<GlobalObject*> global = cx->global();
|
||||||
sc->moduleResolveHook = &args[0].toObject().as<JSFunction>();
|
global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
|
@ -4289,18 +4297,20 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
|
||||||
static JSObject*
|
static JSObject*
|
||||||
CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier)
|
CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier)
|
||||||
{
|
{
|
||||||
ShellContext* sc = GetShellContext(cx);
|
Handle<GlobalObject*> global = cx->global();
|
||||||
if (!sc->moduleResolveHook) {
|
RootedValue hookValue(cx, global->getReservedSlot(GlobalAppSlotModuleResolveHook));
|
||||||
|
if (hookValue.isUndefined()) {
|
||||||
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
|
||||||
|
|
||||||
JS::AutoValueArray<2> args(cx);
|
JS::AutoValueArray<2> args(cx);
|
||||||
args[0].setObject(*module);
|
args[0].setObject(*module);
|
||||||
args[1].setString(specifier);
|
args[1].setString(specifier);
|
||||||
|
|
||||||
RootedValue result(cx);
|
RootedValue result(cx);
|
||||||
if (!JS_CallFunction(cx, nullptr, sc->moduleResolveHook, args, &result))
|
if (!JS_CallFunctionValue(cx, nullptr, hookValue, args, &result))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
|
if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
|
||||||
|
|
|
@ -180,8 +180,6 @@ struct ShellContext
|
||||||
// Off-thread parse state.
|
// Off-thread parse state.
|
||||||
js::Monitor offThreadMonitor;
|
js::Monitor offThreadMonitor;
|
||||||
Vector<OffThreadJob*, 0, SystemAllocPolicy> offThreadJobs;
|
Vector<OffThreadJob*, 0, SystemAllocPolicy> offThreadJobs;
|
||||||
|
|
||||||
JS::PersistentRootedFunction moduleResolveHook;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ShellContext*
|
extern ShellContext*
|
||||||
|
|
|
@ -2742,9 +2742,8 @@ Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx, AbstractFramePtr fr
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
Debugger::ensureExecutionObservabilityOfCompartment(JSContext* cx, JSCompartment* comp)
|
Debugger::ensureExecutionObservabilityOfRealm(JSContext* cx, Realm* realm)
|
||||||
{
|
{
|
||||||
Realm* realm = JS::GetRealmForCompartment(comp);
|
|
||||||
if (realm->debuggerObservesAllExecution())
|
if (realm->debuggerObservesAllExecution())
|
||||||
return true;
|
return true;
|
||||||
ExecutionObservableRealms obs(cx);
|
ExecutionObservableRealms obs(cx);
|
||||||
|
@ -3746,7 +3745,7 @@ Debugger::addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp)
|
||||||
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
|
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
|
||||||
if (r == dbg->object->realm() || r->creationOptions().invisibleToDebugger())
|
if (r == dbg->object->realm() || r->creationOptions().invisibleToDebugger())
|
||||||
continue;
|
continue;
|
||||||
r->scheduledForDestruction = false;
|
JS::GetCompartmentForRealm(r)->scheduledForDestruction = false;
|
||||||
GlobalObject* global = r->maybeGlobal();
|
GlobalObject* global = r->maybeGlobal();
|
||||||
if (global) {
|
if (global) {
|
||||||
Rooted<GlobalObject*> rg(cx, global);
|
Rooted<GlobalObject*> rg(cx, global);
|
||||||
|
@ -4078,7 +4077,7 @@ Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global)
|
||||||
debuggeeRealm->updateDebuggerObservesAsmJS();
|
debuggeeRealm->updateDebuggerObservesAsmJS();
|
||||||
debuggeeRealm->updateDebuggerObservesBinarySource();
|
debuggeeRealm->updateDebuggerObservesBinarySource();
|
||||||
debuggeeRealm->updateDebuggerObservesCoverage();
|
debuggeeRealm->updateDebuggerObservesCoverage();
|
||||||
if (observesAllExecution() && !ensureExecutionObservabilityOfCompartment(cx, debuggeeRealm))
|
if (observesAllExecution() && !ensureExecutionObservabilityOfRealm(cx, debuggeeRealm))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
globalDebuggersGuard.release();
|
globalDebuggersGuard.release();
|
||||||
|
@ -4963,7 +4962,7 @@ Debugger::findAllGlobals(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (r->creationOptions().invisibleToDebugger())
|
if (r->creationOptions().invisibleToDebugger())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
r->scheduledForDestruction = false;
|
JS::GetCompartmentForRealm(r)->scheduledForDestruction = false;
|
||||||
|
|
||||||
GlobalObject* global = r->maybeGlobal();
|
GlobalObject* global = r->maybeGlobal();
|
||||||
|
|
||||||
|
|
|
@ -754,8 +754,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||||
private:
|
private:
|
||||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(JSContext* cx,
|
static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(JSContext* cx,
|
||||||
AbstractFramePtr frame);
|
AbstractFramePtr frame);
|
||||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfCompartment(JSContext* cx,
|
static MOZ_MUST_USE bool ensureExecutionObservabilityOfRealm(JSContext* cx,
|
||||||
JSCompartment* comp);
|
JS::Realm* realm);
|
||||||
|
|
||||||
static bool hookObservesAllExecution(Hook which);
|
static bool hookObservesAllExecution(Hook which);
|
||||||
|
|
||||||
|
|
|
@ -308,10 +308,10 @@ DebuggerMemory::setAllocationSamplingProbability(JSContext* cx, unsigned argc, V
|
||||||
dbg->allocationSamplingProbability = probability;
|
dbg->allocationSamplingProbability = probability;
|
||||||
|
|
||||||
// If this is a change any debuggees would observe, have all debuggee
|
// If this is a change any debuggees would observe, have all debuggee
|
||||||
// compartments recompute their sampling probabilities.
|
// realms recompute their sampling probabilities.
|
||||||
if (dbg->enabled && dbg->trackingAllocationSites) {
|
if (dbg->enabled && dbg->trackingAllocationSites) {
|
||||||
for (auto r = dbg->debuggees.all(); !r.empty(); r.popFront())
|
for (auto r = dbg->debuggees.all(); !r.empty(); r.popFront())
|
||||||
r.front()->compartment()->chooseAllocationSamplingProbability();
|
r.front()->realm()->chooseAllocationSamplingProbability();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,9 @@ CallObject::createSingleton(JSContext* cx, HandleShape shape)
|
||||||
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
|
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
|
||||||
kind = gc::GetBackgroundAllocKind(kind);
|
kind = gc::GetBackgroundAllocKind(kind);
|
||||||
|
|
||||||
RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(cx, &class_, TaggedProto(nullptr)));
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
|
RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(cx, realm, &class_,
|
||||||
|
TaggedProto(nullptr)));
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -2314,7 +2316,7 @@ const DebugEnvironmentProxyHandler DebugEnvironmentProxyHandler::singleton;
|
||||||
/* static */ DebugEnvironmentProxy*
|
/* static */ DebugEnvironmentProxy*
|
||||||
DebugEnvironmentProxy::create(JSContext* cx, EnvironmentObject& env, HandleObject enclosing)
|
DebugEnvironmentProxy::create(JSContext* cx, EnvironmentObject& env, HandleObject enclosing)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(env.compartment() == cx->compartment());
|
MOZ_ASSERT(env.realm() == cx->realm());
|
||||||
MOZ_ASSERT(!enclosing->is<EnvironmentObject>());
|
MOZ_ASSERT(!enclosing->is<EnvironmentObject>());
|
||||||
|
|
||||||
RootedValue priv(cx, ObjectValue(env));
|
RootedValue priv(cx, ObjectValue(env));
|
||||||
|
@ -2514,11 +2516,11 @@ CanUseDebugEnvironmentMaps(JSContext* cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugEnvironments*
|
DebugEnvironments*
|
||||||
DebugEnvironments::ensureCompartmentData(JSContext* cx)
|
DebugEnvironments::ensureRealmData(JSContext* cx)
|
||||||
{
|
{
|
||||||
JSCompartment* c = cx->compartment();
|
Realm* realm = cx->realm();
|
||||||
if (c->debugEnvs)
|
if (auto* debugEnvs = realm->debugEnvs())
|
||||||
return c->debugEnvs.get();
|
return debugEnvs;
|
||||||
|
|
||||||
auto debugEnvs = cx->make_unique<DebugEnvironments>(cx, cx->zone());
|
auto debugEnvs = cx->make_unique<DebugEnvironments>(cx, cx->zone());
|
||||||
if (!debugEnvs || !debugEnvs->init()) {
|
if (!debugEnvs || !debugEnvs->init()) {
|
||||||
|
@ -2526,14 +2528,14 @@ DebugEnvironments::ensureCompartmentData(JSContext* cx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->debugEnvs = Move(debugEnvs);
|
realm->debugEnvsRef() = Move(debugEnvs);
|
||||||
return c->debugEnvs.get();
|
return realm->debugEnvs();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ DebugEnvironmentProxy*
|
/* static */ DebugEnvironmentProxy*
|
||||||
DebugEnvironments::hasDebugEnvironment(JSContext* cx, EnvironmentObject& env)
|
DebugEnvironments::hasDebugEnvironment(JSContext* cx, EnvironmentObject& env)
|
||||||
{
|
{
|
||||||
DebugEnvironments* envs = env.compartment()->debugEnvs.get();
|
DebugEnvironments* envs = env.realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -2549,13 +2551,13 @@ DebugEnvironments::hasDebugEnvironment(JSContext* cx, EnvironmentObject& env)
|
||||||
DebugEnvironments::addDebugEnvironment(JSContext* cx, Handle<EnvironmentObject*> env,
|
DebugEnvironments::addDebugEnvironment(JSContext* cx, Handle<EnvironmentObject*> env,
|
||||||
Handle<DebugEnvironmentProxy*> debugEnv)
|
Handle<DebugEnvironmentProxy*> debugEnv)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(cx->compartment() == env->compartment());
|
MOZ_ASSERT(cx->realm() == env->realm());
|
||||||
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
|
MOZ_ASSERT(cx->realm() == debugEnv->realm());
|
||||||
|
|
||||||
if (!CanUseDebugEnvironmentMaps(cx))
|
if (!CanUseDebugEnvironmentMaps(cx))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
DebugEnvironments* envs = ensureCompartmentData(cx);
|
DebugEnvironments* envs = ensureRealmData(cx);
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -2567,7 +2569,7 @@ DebugEnvironments::hasDebugEnvironment(JSContext* cx, const EnvironmentIter& ei)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
||||||
|
|
||||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -2583,7 +2585,7 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
|
||||||
Handle<DebugEnvironmentProxy*> debugEnv)
|
Handle<DebugEnvironmentProxy*> debugEnv)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
||||||
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
|
MOZ_ASSERT(cx->realm() == debugEnv->realm());
|
||||||
// Generators should always have environments.
|
// Generators should always have environments.
|
||||||
MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
|
MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
|
||||||
!ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator() &&
|
!ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator() &&
|
||||||
|
@ -2592,7 +2594,7 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
|
||||||
if (!CanUseDebugEnvironmentMaps(cx))
|
if (!CanUseDebugEnvironmentMaps(cx))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
DebugEnvironments* envs = ensureCompartmentData(cx);
|
DebugEnvironments* envs = ensureRealmData(cx);
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -2717,7 +2719,7 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame)
|
||||||
{
|
{
|
||||||
assertSameCompartment(cx, frame);
|
assertSameCompartment(cx, frame);
|
||||||
|
|
||||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2759,7 +2761,7 @@ DebugEnvironments::onPopLexical(JSContext* cx, AbstractFramePtr frame, jsbytecod
|
||||||
{
|
{
|
||||||
assertSameCompartment(cx, frame);
|
assertSameCompartment(cx, frame);
|
||||||
|
|
||||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2771,7 +2773,7 @@ template <typename Environment, typename Scope>
|
||||||
void
|
void
|
||||||
DebugEnvironments::onPopGeneric(JSContext* cx, const EnvironmentIter& ei)
|
DebugEnvironments::onPopGeneric(JSContext* cx, const EnvironmentIter& ei)
|
||||||
{
|
{
|
||||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2807,7 +2809,7 @@ DebugEnvironments::onPopVar(JSContext* cx, AbstractFramePtr frame, jsbytecode* p
|
||||||
{
|
{
|
||||||
assertSameCompartment(cx, frame);
|
assertSameCompartment(cx, frame);
|
||||||
|
|
||||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2827,14 +2829,15 @@ DebugEnvironments::onPopVar(JSContext* cx, const EnvironmentIter& ei)
|
||||||
void
|
void
|
||||||
DebugEnvironments::onPopWith(AbstractFramePtr frame)
|
DebugEnvironments::onPopWith(AbstractFramePtr frame)
|
||||||
{
|
{
|
||||||
if (DebugEnvironments* envs = frame.compartment()->debugEnvs.get())
|
Realm* realm = JS::GetRealmForCompartment(frame.compartment());
|
||||||
|
if (DebugEnvironments* envs = realm->debugEnvs())
|
||||||
envs->liveEnvs.remove(&frame.environmentChain()->as<WithEnvironmentObject>());
|
envs->liveEnvs.remove(&frame.environmentChain()->as<WithEnvironmentObject>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DebugEnvironments::onCompartmentUnsetIsDebuggee(JSCompartment* c)
|
DebugEnvironments::onRealmUnsetIsDebuggee(Realm* realm)
|
||||||
{
|
{
|
||||||
if (DebugEnvironments* envs = c->debugEnvs.get()) {
|
if (DebugEnvironments* envs = realm->debugEnvs()) {
|
||||||
envs->proxiedEnvs.clear();
|
envs->proxiedEnvs.clear();
|
||||||
envs->missingEnvs.clear();
|
envs->missingEnvs.clear();
|
||||||
envs->liveEnvs.clear();
|
envs->liveEnvs.clear();
|
||||||
|
@ -2864,7 +2867,7 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AbstractFramePtr frame = i.abstractFramePtr();
|
AbstractFramePtr frame = i.abstractFramePtr();
|
||||||
if (frame.environmentChain()->compartment() != cx->compartment())
|
if (frame.environmentChain()->realm() != cx->realm())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (frame.isFunctionFrame()) {
|
if (frame.isFunctionFrame()) {
|
||||||
|
@ -2882,8 +2885,8 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
||||||
|
|
||||||
for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame(); ei++) {
|
for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame(); ei++) {
|
||||||
if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) {
|
if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) {
|
||||||
MOZ_ASSERT(ei.environment().compartment() == cx->compartment());
|
MOZ_ASSERT(ei.environment().realm() == cx->realm());
|
||||||
DebugEnvironments* envs = ensureCompartmentData(cx);
|
DebugEnvironments* envs = ensureRealmData(cx);
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return false;
|
return false;
|
||||||
if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
|
if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
|
||||||
|
@ -2903,7 +2906,7 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
||||||
LiveEnvironmentVal*
|
LiveEnvironmentVal*
|
||||||
DebugEnvironments::hasLiveEnvironment(EnvironmentObject& env)
|
DebugEnvironments::hasLiveEnvironment(EnvironmentObject& env)
|
||||||
{
|
{
|
||||||
DebugEnvironments* envs = env.compartment()->debugEnvs.get();
|
DebugEnvironments* envs = env.realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -2932,7 +2935,7 @@ DebugEnvironments::unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr until)
|
||||||
if (frame == until)
|
if (frame == until)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (frame.environmentChain()->compartment() != cx->compartment())
|
if (frame.environmentChain()->realm() != cx->realm())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
frame.unsetPrevUpToDate();
|
frame.unsetPrevUpToDate();
|
||||||
|
@ -2942,7 +2945,7 @@ DebugEnvironments::unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr until)
|
||||||
/* static */ void
|
/* static */ void
|
||||||
DebugEnvironments::forwardLiveFrame(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to)
|
DebugEnvironments::forwardLiveFrame(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to)
|
||||||
{
|
{
|
||||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||||
if (!envs)
|
if (!envs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -952,7 +952,7 @@ class DebugEnvironmentProxy : public ProxyObject
|
||||||
bool isOptimizedOut() const;
|
bool isOptimizedOut() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Maintains per-compartment debug environment bookkeeping information. */
|
/* Maintains per-realm debug environment bookkeeping information. */
|
||||||
class DebugEnvironments
|
class DebugEnvironments
|
||||||
{
|
{
|
||||||
Zone* zone_;
|
Zone* zone_;
|
||||||
|
@ -993,7 +993,7 @@ class DebugEnvironments
|
||||||
private:
|
private:
|
||||||
bool init();
|
bool init();
|
||||||
|
|
||||||
static DebugEnvironments* ensureCompartmentData(JSContext* cx);
|
static DebugEnvironments* ensureRealmData(JSContext* cx);
|
||||||
|
|
||||||
template <typename Environment, typename Scope>
|
template <typename Environment, typename Scope>
|
||||||
static void onPopGeneric(JSContext* cx, const EnvironmentIter& ei);
|
static void onPopGeneric(JSContext* cx, const EnvironmentIter& ei);
|
||||||
|
@ -1044,7 +1044,7 @@ class DebugEnvironments
|
||||||
static void onPopLexical(JSContext* cx, const EnvironmentIter& ei);
|
static void onPopLexical(JSContext* cx, const EnvironmentIter& ei);
|
||||||
static void onPopLexical(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
|
static void onPopLexical(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
|
||||||
static void onPopWith(AbstractFramePtr frame);
|
static void onPopWith(AbstractFramePtr frame);
|
||||||
static void onCompartmentUnsetIsDebuggee(JSCompartment* c);
|
static void onRealmUnsetIsDebuggee(Realm* realm);
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
|
@ -346,10 +346,10 @@ GlobalObject::resolveOffThreadConstructor(JSContext* cx,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((key == JSProto_Object || key == JSProto_Function || key == JSProto_Array) &&
|
if (key == JSProto_Object || key == JSProto_Function || key == JSProto_Array) {
|
||||||
!JSObject::setNewGroupUnknown(cx, placeholder->getClass(), placeholder))
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
{
|
if (!JSObject::setNewGroupUnknown(cx, realm, placeholder->getClass(), placeholder))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
global->setPrototype(key, ObjectValue(*placeholder));
|
global->setPrototype(key, ObjectValue(*placeholder));
|
||||||
|
|
|
@ -1660,11 +1660,11 @@ class ReservedRooted : public RootedBase<T, ReservedRooted<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit ReservedRooted(Rooted<T>* root) : savedRoot(root) {
|
explicit ReservedRooted(Rooted<T>* root) : savedRoot(root) {
|
||||||
*root = JS::GCPolicy<T>::initial();
|
*root = JS::SafelyInitialized<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ReservedRooted() {
|
~ReservedRooted() {
|
||||||
*savedRoot = JS::GCPolicy<T>::initial();
|
*savedRoot = JS::SafelyInitialized<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const T& p) const { *savedRoot = p; }
|
void set(const T& p) const { *savedRoot = p; }
|
||||||
|
|
|
@ -971,8 +971,8 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
|
||||||
|
|
||||||
// Create a new group for the template.
|
// Create a new group for the template.
|
||||||
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
||||||
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(),
|
RootedObjectGroup group(cx, ObjectGroupRealm::makeGroup(cx, templateObject->getClass(),
|
||||||
proto));
|
proto));
|
||||||
if (!group)
|
if (!group)
|
||||||
return iterResultTemplate_; // = nullptr
|
return iterResultTemplate_; // = nullptr
|
||||||
templateObject->setGroup(group);
|
templateObject->setGroup(group);
|
||||||
|
|
|
@ -43,10 +43,7 @@ using mozilla::PodArrayZero;
|
||||||
|
|
||||||
JSCompartment::JSCompartment(Zone* zone)
|
JSCompartment::JSCompartment(Zone* zone)
|
||||||
: zone_(zone),
|
: zone_(zone),
|
||||||
runtime_(zone->runtimeFromAnyThread()),
|
runtime_(zone->runtimeFromAnyThread())
|
||||||
data(nullptr),
|
|
||||||
regExps(),
|
|
||||||
gcIncomingGrayPointers(nullptr)
|
|
||||||
{
|
{
|
||||||
runtime_->numCompartments++;
|
runtime_->numCompartments++;
|
||||||
}
|
}
|
||||||
|
@ -80,17 +77,17 @@ Realm::~Realm()
|
||||||
JSRuntime* rt = runtimeFromMainThread();
|
JSRuntime* rt = runtimeFromMainThread();
|
||||||
if (rt->lcovOutput().isEnabled())
|
if (rt->lcovOutput().isEnabled())
|
||||||
rt->lcovOutput().writeLCovResult(lcovOutput);
|
rt->lcovOutput().writeLCovResult(lcovOutput);
|
||||||
}
|
|
||||||
|
|
||||||
JSCompartment::~JSCompartment()
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Avoid assertion destroying the unboxed layouts list if the embedding
|
// Avoid assertion destroying the unboxed layouts list if the embedding
|
||||||
// leaked GC things.
|
// leaked GC things.
|
||||||
if (!runtime_->gc.shutdownCollectedEverything())
|
if (!runtime_->gc.shutdownCollectedEverything())
|
||||||
unboxedLayouts.clear();
|
objectGroups_.unboxedLayouts.clear();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
JSCompartment::~JSCompartment()
|
||||||
|
{
|
||||||
runtime_->numCompartments--;
|
runtime_->numCompartments--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,8 +707,8 @@ Realm::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMa
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Mark debug scopes, if present */
|
/* Mark debug scopes, if present */
|
||||||
if (debugEnvs)
|
if (debugEnvs_)
|
||||||
debugEnvs->trace(trc);
|
debugEnvs_->trace(trc);
|
||||||
|
|
||||||
objects_.trace(trc);
|
objects_.trace(trc);
|
||||||
|
|
||||||
|
@ -757,8 +754,8 @@ ObjectRealm::finishRoots()
|
||||||
void
|
void
|
||||||
Realm::finishRoots()
|
Realm::finishRoots()
|
||||||
{
|
{
|
||||||
if (debugEnvs)
|
if (debugEnvs_)
|
||||||
debugEnvs->finish();
|
debugEnvs_->finish();
|
||||||
|
|
||||||
objects_.finishRoots();
|
objects_.finishRoots();
|
||||||
|
|
||||||
|
@ -792,7 +789,7 @@ JSCompartment::sweepAfterMinorGC(JSTracer* trc)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSCompartment::sweepSavedStacks()
|
Realm::sweepSavedStacks()
|
||||||
{
|
{
|
||||||
savedStacks_.sweep();
|
savedStacks_.sweep();
|
||||||
}
|
}
|
||||||
|
@ -822,7 +819,7 @@ Realm::sweepJitRealm()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSCompartment::sweepRegExps()
|
Realm::sweepRegExps()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* JIT code increments activeWarmUpCounter for any RegExpShared used by jit
|
* JIT code increments activeWarmUpCounter for any RegExpShared used by jit
|
||||||
|
@ -833,10 +830,10 @@ JSCompartment::sweepRegExps()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSCompartment::sweepDebugEnvironments()
|
Realm::sweepDebugEnvironments()
|
||||||
{
|
{
|
||||||
if (debugEnvs)
|
if (debugEnvs_)
|
||||||
debugEnvs->sweep();
|
debugEnvs_->sweep();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -932,17 +929,22 @@ JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Realm::fixupAfterMovingGC()
|
||||||
|
{
|
||||||
|
purge();
|
||||||
|
fixupGlobal();
|
||||||
|
objectGroups_.fixupTablesAfterMovingGC();
|
||||||
|
fixupScriptMapsAfterMovingGC();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSCompartment::fixupAfterMovingGC()
|
JSCompartment::fixupAfterMovingGC()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(zone()->isGCCompacting());
|
MOZ_ASSERT(zone()->isGCCompacting());
|
||||||
|
|
||||||
Realm* realm = JS::GetRealmForCompartment(this);
|
Realm* realm = JS::GetRealmForCompartment(this);
|
||||||
|
realm->fixupAfterMovingGC();
|
||||||
realm->purge();
|
|
||||||
realm->fixupGlobal();
|
|
||||||
objectGroups.fixupTablesAfterMovingGC();
|
|
||||||
realm->fixupScriptMapsAfterMovingGC();
|
|
||||||
|
|
||||||
// Sweep the wrapper map to update values (wrapper objects) in this
|
// Sweep the wrapper map to update values (wrapper objects) in this
|
||||||
// compartment that may have been moved.
|
// compartment that may have been moved.
|
||||||
|
@ -1035,7 +1037,7 @@ Realm::purge()
|
||||||
{
|
{
|
||||||
dtoaCache.purge();
|
dtoaCache.purge();
|
||||||
newProxyCache.purge();
|
newProxyCache.purge();
|
||||||
objectGroups.purge();
|
objectGroups_.purge();
|
||||||
objects_.iteratorCache.clearAndShrink();
|
objects_.iteratorCache.clearAndShrink();
|
||||||
arraySpeciesLookup.purge();
|
arraySpeciesLookup.purge();
|
||||||
}
|
}
|
||||||
|
@ -1049,10 +1051,10 @@ Realm::clearTables()
|
||||||
// a realm that has been used off thread into another realm and zone.
|
// a realm that has been used off thread into another realm and zone.
|
||||||
JS::GetCompartmentForRealm(this)->assertNoCrossCompartmentWrappers();
|
JS::GetCompartmentForRealm(this)->assertNoCrossCompartmentWrappers();
|
||||||
MOZ_ASSERT(!jitRealm_);
|
MOZ_ASSERT(!jitRealm_);
|
||||||
MOZ_ASSERT(!debugEnvs);
|
MOZ_ASSERT(!debugEnvs_);
|
||||||
MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
|
MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
|
||||||
|
|
||||||
objectGroups.clearTables();
|
objectGroups_.clearTables();
|
||||||
if (savedStacks_.initialized())
|
if (savedStacks_.initialized())
|
||||||
savedStacks_.clear();
|
savedStacks_.clear();
|
||||||
if (varNames_.initialized())
|
if (varNames_.initialized())
|
||||||
|
@ -1086,7 +1088,7 @@ void
|
||||||
Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
|
Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(obj->realm() == this);
|
MOZ_ASSERT(obj->realm() == this);
|
||||||
assertSameCompartment(cx, this, obj);
|
assertSameCompartment(cx, JS::GetCompartmentForRealm(this), obj);
|
||||||
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||||
if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
|
if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
|
||||||
|
@ -1239,7 +1241,7 @@ Realm::unsetIsDebuggee()
|
||||||
{
|
{
|
||||||
if (isDebuggee()) {
|
if (isDebuggee()) {
|
||||||
debugModeBits_ &= ~DebuggerObservesMask;
|
debugModeBits_ &= ~DebuggerObservesMask;
|
||||||
DebugEnvironments::onCompartmentUnsetIsDebuggee(this);
|
DebugEnvironments::onRealmUnsetIsDebuggee(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1371,9 +1373,9 @@ Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||||
JSCompartment::addSizeOfExcludingThis(mallocSizeOf, crossCompartmentWrappersArg);
|
JSCompartment::addSizeOfExcludingThis(mallocSizeOf, crossCompartmentWrappersArg);
|
||||||
|
|
||||||
*realmObject += mallocSizeOf(this);
|
*realmObject += mallocSizeOf(this);
|
||||||
objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
|
objectGroups_.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
|
||||||
tiArrayTypeTables, tiObjectTypeTables,
|
tiArrayTypeTables, tiObjectTypeTables,
|
||||||
realmTables);
|
realmTables);
|
||||||
wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
|
wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
|
||||||
|
|
||||||
objects_.addSizeOfExcludingThis(mallocSizeOf,
|
objects_.addSizeOfExcludingThis(mallocSizeOf,
|
||||||
|
|
|
@ -557,10 +557,27 @@ struct JSCompartment
|
||||||
JSRuntime* runtime_;
|
JSRuntime* runtime_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct JSRuntime;
|
js::WrapperMap crossCompartmentWrappers;
|
||||||
friend struct JSContext;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/*
|
||||||
|
* During GC, stores the head of a list of incoming pointers from gray cells.
|
||||||
|
*
|
||||||
|
* The objects in the list are either cross-compartment wrappers, or
|
||||||
|
* debugger wrapper objects. The list link is either in the second extra
|
||||||
|
* slot for the former, or a special slot for the latter.
|
||||||
|
*/
|
||||||
|
JSObject* gcIncomingGrayPointers = nullptr;
|
||||||
|
|
||||||
|
void* data = nullptr;
|
||||||
|
|
||||||
|
// These flags help us to discover if a compartment that shouldn't be alive
|
||||||
|
// manages to outlive a GC. Note that these flags have to be on the
|
||||||
|
// compartment, not the realm, because same-compartment realms can have
|
||||||
|
// cross-realm pointers without wrappers.
|
||||||
|
bool scheduledForDestruction = false;
|
||||||
|
bool maybeAlive = true;
|
||||||
|
|
||||||
JS::Zone* zone() { return zone_; }
|
JS::Zone* zone() { return zone_; }
|
||||||
const JS::Zone* zone() const { return zone_; }
|
const JS::Zone* zone() const { return zone_; }
|
||||||
|
|
||||||
|
@ -575,53 +592,19 @@ struct JSCompartment
|
||||||
return runtime_;
|
return runtime_;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
void* data;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
js::SavedStacks savedStacks_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
js::WrapperMap crossCompartmentWrappers;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void assertNoCrossCompartmentWrappers() {
|
void assertNoCrossCompartmentWrappers() {
|
||||||
MOZ_ASSERT(crossCompartmentWrappers.empty());
|
MOZ_ASSERT(crossCompartmentWrappers.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
js::RegExpCompartment regExps;
|
|
||||||
|
|
||||||
// Recompute the probability with which this compartment should record
|
|
||||||
// profiling data (stack traces, allocations log, etc.) about each
|
|
||||||
// allocation. We consult the probabilities requested by the Debugger
|
|
||||||
// instances observing us, if any.
|
|
||||||
void chooseAllocationSamplingProbability() { savedStacks_.chooseSamplingProbability(this); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||||
size_t* crossCompartmentWrappersArg);
|
size_t* crossCompartmentWrappersArg);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Object group tables and other state in the compartment.
|
|
||||||
js::ObjectGroupCompartment objectGroups;
|
|
||||||
|
|
||||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||||
void checkWrapperMapAfterMovingGC();
|
void checkWrapperMapAfterMovingGC();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// All unboxed layouts in the compartment.
|
|
||||||
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* During GC, stores the head of a list of incoming pointers from gray cells.
|
|
||||||
*
|
|
||||||
* The objects in the list are either cross-compartment wrappers, or
|
|
||||||
* debugger wrapper objects. The list link is either in the second extra
|
|
||||||
* slot for the former, or a special slot for the latter.
|
|
||||||
*/
|
|
||||||
JSObject* gcIncomingGrayPointers;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
|
bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
|
||||||
bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
|
bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
|
||||||
|
@ -689,30 +672,11 @@ struct JSCompartment
|
||||||
void sweepAfterMinorGC(JSTracer* trc);
|
void sweepAfterMinorGC(JSTracer* trc);
|
||||||
|
|
||||||
void sweepCrossCompartmentWrappers();
|
void sweepCrossCompartmentWrappers();
|
||||||
void sweepSavedStacks();
|
|
||||||
void sweepRegExps();
|
|
||||||
void sweepDebugEnvironments();
|
|
||||||
|
|
||||||
static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
|
static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
|
||||||
void fixupAfterMovingGC();
|
void fixupAfterMovingGC();
|
||||||
|
|
||||||
js::SavedStacks& savedStacks() { return savedStacks_; }
|
|
||||||
|
|
||||||
void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
|
void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
|
||||||
|
|
||||||
static size_t offsetOfRegExps() {
|
|
||||||
return offsetof(JSCompartment, regExps);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bookkeeping information for debug scope objects. */
|
|
||||||
js::UniquePtr<js::DebugEnvironments> debugEnvs;
|
|
||||||
|
|
||||||
// These flags help us to discover if a compartment that shouldn't be alive
|
|
||||||
// manages to outlive a GC. Note that these flags have to be on the
|
|
||||||
// compartment, not the realm, because same-compartment realms can have
|
|
||||||
// cross-realm pointers without wrappers.
|
|
||||||
bool scheduledForDestruction = false;
|
|
||||||
bool maybeAlive = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
@ -782,7 +746,7 @@ class ObjectRealm
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
class JS::Realm : public JSCompartment
|
class JS::Realm : private JSCompartment
|
||||||
{
|
{
|
||||||
const JS::RealmCreationOptions creationOptions_;
|
const JS::RealmCreationOptions creationOptions_;
|
||||||
JS::RealmBehaviors behaviors_;
|
JS::RealmBehaviors behaviors_;
|
||||||
|
@ -794,6 +758,12 @@ class JS::Realm : public JSCompartment
|
||||||
js::ObjectRealm objects_;
|
js::ObjectRealm objects_;
|
||||||
friend js::ObjectRealm& js::ObjectRealm::get(const JSObject*);
|
friend js::ObjectRealm& js::ObjectRealm::get(const JSObject*);
|
||||||
|
|
||||||
|
// Object group tables and other state in the realm. This is private to
|
||||||
|
// enforce use of ObjectGroupRealm::get(group)/getForNewObject(cx).
|
||||||
|
js::ObjectGroupRealm objectGroups_;
|
||||||
|
friend js::ObjectGroupRealm& js::ObjectGroupRealm::get(js::ObjectGroup* group);
|
||||||
|
friend js::ObjectGroupRealm& js::ObjectGroupRealm::getForNewObject(JSContext* cx);
|
||||||
|
|
||||||
// The global environment record's [[VarNames]] list that contains all
|
// The global environment record's [[VarNames]] list that contains all
|
||||||
// names declared using FunctionDeclaration, GeneratorDeclaration, and
|
// names declared using FunctionDeclaration, GeneratorDeclaration, and
|
||||||
// VariableDeclaration declarations in global code in this realm.
|
// VariableDeclaration declarations in global code in this realm.
|
||||||
|
@ -817,6 +787,11 @@ class JS::Realm : public JSCompartment
|
||||||
|
|
||||||
js::UniquePtr<js::jit::JitRealm> jitRealm_;
|
js::UniquePtr<js::jit::JitRealm> jitRealm_;
|
||||||
|
|
||||||
|
// Bookkeeping information for debug scope objects.
|
||||||
|
js::UniquePtr<js::DebugEnvironments> debugEnvs_;
|
||||||
|
|
||||||
|
js::SavedStacks savedStacks_;
|
||||||
|
|
||||||
// Used by memory reporters and invalid otherwise.
|
// Used by memory reporters and invalid otherwise.
|
||||||
JS::RealmStats* realmStats_ = nullptr;
|
JS::RealmStats* realmStats_ = nullptr;
|
||||||
|
|
||||||
|
@ -863,6 +838,8 @@ class JS::Realm : public JSCompartment
|
||||||
// is enabled.
|
// is enabled.
|
||||||
js::coverage::LCovRealm lcovOutput;
|
js::coverage::LCovRealm lcovOutput;
|
||||||
|
|
||||||
|
js::RegExpRealm regExps;
|
||||||
|
|
||||||
js::DtoaCache dtoaCache;
|
js::DtoaCache dtoaCache;
|
||||||
js::NewProxyCache newProxyCache;
|
js::NewProxyCache newProxyCache;
|
||||||
js::ArraySpeciesLookup arraySpeciesLookup;
|
js::ArraySpeciesLookup arraySpeciesLookup;
|
||||||
|
@ -927,6 +904,24 @@ class JS::Realm : public JSCompartment
|
||||||
size_t* privateData,
|
size_t* privateData,
|
||||||
size_t* scriptCountsMapArg);
|
size_t* scriptCountsMapArg);
|
||||||
|
|
||||||
|
JS::Zone* zone() {
|
||||||
|
return zone_;
|
||||||
|
}
|
||||||
|
const JS::Zone* zone() const {
|
||||||
|
return zone_;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSRuntime* runtimeFromMainThread() const {
|
||||||
|
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
|
||||||
|
return runtime_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Unrestricted access to the runtime from an arbitrary thread
|
||||||
|
// can easily lead to races. Use this method very carefully.
|
||||||
|
JSRuntime* runtimeFromAnyThread() const {
|
||||||
|
return runtime_;
|
||||||
|
}
|
||||||
|
|
||||||
const JS::RealmCreationOptions& creationOptions() const { return creationOptions_; }
|
const JS::RealmCreationOptions& creationOptions() const { return creationOptions_; }
|
||||||
JS::RealmBehaviors& behaviors() { return behaviors_; }
|
JS::RealmBehaviors& behaviors() { return behaviors_; }
|
||||||
const JS::RealmBehaviors& behaviors() const { return behaviors_; }
|
const JS::RealmBehaviors& behaviors() const { return behaviors_; }
|
||||||
|
@ -987,18 +982,28 @@ class JS::Realm : public JSCompartment
|
||||||
void finishRoots();
|
void finishRoots();
|
||||||
|
|
||||||
void sweepAfterMinorGC();
|
void sweepAfterMinorGC();
|
||||||
|
void sweepDebugEnvironments();
|
||||||
void sweepObjectRealm();
|
void sweepObjectRealm();
|
||||||
|
void sweepRegExps();
|
||||||
void sweepSelfHostingScriptSource();
|
void sweepSelfHostingScriptSource();
|
||||||
void sweepTemplateObjects();
|
void sweepTemplateObjects();
|
||||||
|
|
||||||
|
void sweepObjectGroups() {
|
||||||
|
objectGroups_.sweep();
|
||||||
|
}
|
||||||
|
|
||||||
void clearScriptCounts();
|
void clearScriptCounts();
|
||||||
void clearScriptNames();
|
void clearScriptNames();
|
||||||
|
|
||||||
void purge();
|
void purge();
|
||||||
|
|
||||||
|
void fixupAfterMovingGC();
|
||||||
void fixupScriptMapsAfterMovingGC();
|
void fixupScriptMapsAfterMovingGC();
|
||||||
|
|
||||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||||
|
void checkObjectGroupTablesAfterMovingGC() {
|
||||||
|
objectGroups_.checkTablesAfterMovingGC();
|
||||||
|
}
|
||||||
void checkScriptMapsAfterMovingGC();
|
void checkScriptMapsAfterMovingGC();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1266,6 +1271,31 @@ class JS::Realm : public JSCompartment
|
||||||
js::jit::JitRealm* jitRealm() {
|
js::jit::JitRealm* jitRealm() {
|
||||||
return jitRealm_.get();
|
return jitRealm_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
js::DebugEnvironments* debugEnvs() {
|
||||||
|
return debugEnvs_.get();
|
||||||
|
}
|
||||||
|
js::UniquePtr<js::DebugEnvironments>& debugEnvsRef() {
|
||||||
|
return debugEnvs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
js::SavedStacks& savedStacks() {
|
||||||
|
return savedStacks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recompute the probability with which this realm should record
|
||||||
|
// profiling data (stack traces, allocations log, etc.) about each
|
||||||
|
// allocation. We consult the probabilities requested by the Debugger
|
||||||
|
// instances observing us, if any.
|
||||||
|
void chooseAllocationSamplingProbability() {
|
||||||
|
savedStacks_.chooseSamplingProbability(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sweepSavedStacks();
|
||||||
|
|
||||||
|
static constexpr size_t offsetOfRegExps() {
|
||||||
|
return offsetof(JS::Realm, regExps);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
|
@ -166,7 +166,6 @@ class CompartmentChecker
|
||||||
|
|
||||||
void check(InterpreterFrame* fp);
|
void check(InterpreterFrame* fp);
|
||||||
void check(AbstractFramePtr frame);
|
void check(AbstractFramePtr frame);
|
||||||
void check(SavedStacks* stacks);
|
|
||||||
|
|
||||||
void check(Handle<PropertyDescriptor> desc) {
|
void check(Handle<PropertyDescriptor> desc) {
|
||||||
check(desc.object());
|
check(desc.object());
|
||||||
|
|
|
@ -890,7 +890,8 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
|
||||||
* inference to have unknown properties, to simplify handling of e.g.
|
* inference to have unknown properties, to simplify handling of e.g.
|
||||||
* NewFunctionClone.
|
* NewFunctionClone.
|
||||||
*/
|
*/
|
||||||
if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto))
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
|
if (!JSObject::setNewGroupUnknown(cx, realm, &JSFunction::class_, functionProto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return functionProto;
|
return functionProto;
|
||||||
|
|
|
@ -158,7 +158,8 @@ JSObject::setSingleton(JSContext* cx, js::HandleObject obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||||
|
|
||||||
js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
|
js::ObjectGroupRealm& realm = js::ObjectGroupRealm::get(obj->group_);
|
||||||
|
js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, realm, obj->getClass(),
|
||||||
obj->taggedProto());
|
obj->taggedProto());
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2108,7 +2108,7 @@ SetClassAndProto(JSContext* cx, HandleObject obj,
|
||||||
// group so we can keep track of the interpreted function for Ion
|
// group so we can keep track of the interpreted function for Ion
|
||||||
// inlining.
|
// inlining.
|
||||||
MOZ_ASSERT(obj->is<JSFunction>());
|
MOZ_ASSERT(obj->is<JSFunction>());
|
||||||
newGroup = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, proto);
|
newGroup = ObjectGroupRealm::makeGroup(cx, &JSFunction::class_, proto);
|
||||||
if (!newGroup)
|
if (!newGroup)
|
||||||
return false;
|
return false;
|
||||||
newGroup->setInterpretedFunction(oldGroup->maybeInterpretedFunction());
|
newGroup->setInterpretedFunction(oldGroup->maybeInterpretedFunction());
|
||||||
|
@ -2144,7 +2144,8 @@ JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
|
||||||
|
|
||||||
MarkObjectGroupUnknownProperties(cx, obj->group());
|
MarkObjectGroupUnknownProperties(cx, obj->group());
|
||||||
|
|
||||||
ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
|
ObjectGroupRealm& realm = ObjectGroupRealm::get(obj->group());
|
||||||
|
ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, realm, obj->getClass(),
|
||||||
obj->taggedProto());
|
obj->taggedProto());
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -390,7 +390,8 @@ class JSObject : public js::gc::Cell
|
||||||
* properties.
|
* properties.
|
||||||
*/
|
*/
|
||||||
inline bool isNewGroupUnknown() const;
|
inline bool isNewGroupUnknown() const;
|
||||||
static bool setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj);
|
static bool setNewGroupUnknown(JSContext* cx, js::ObjectGroupRealm& realm,
|
||||||
|
const js::Class* clasp, JS::HandleObject obj);
|
||||||
|
|
||||||
/* Set a new prototype for an object with a singleton type. */
|
/* Set a new prototype for an object with a singleton type. */
|
||||||
static bool splicePrototype(JSContext* cx, js::HandleObject obj, const js::Class* clasp,
|
static bool splicePrototype(JSContext* cx, js::HandleObject obj, const js::Class* clasp,
|
||||||
|
|
|
@ -322,8 +322,8 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
|
||||||
initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
|
initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
|
||||||
|
|
||||||
Rooted<TaggedProto> proto(cx, obj->taggedProto());
|
Rooted<TaggedProto> proto(cx, obj->taggedProto());
|
||||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto,
|
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, obj->getClass(), proto,
|
||||||
initialFlags);
|
initialFlags);
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -340,26 +340,27 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
JSObject::setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj)
|
JSObject::setNewGroupUnknown(JSContext* cx, ObjectGroupRealm& realm, const js::Class* clasp,
|
||||||
|
JS::HandleObject obj)
|
||||||
{
|
{
|
||||||
ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj);
|
ObjectGroup::setDefaultNewGroupUnknown(cx, realm, clasp, obj);
|
||||||
return JSObject::setFlags(cx, obj, BaseShape::NEW_GROUP_UNKNOWN);
|
return JSObject::setFlags(cx, obj, BaseShape::NEW_GROUP_UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// ObjectGroupCompartment NewTable
|
// ObjectGroupRealm NewTable
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Entries for the per-compartment set of groups which are the default
|
* Entries for the per-realm set of groups which are the default
|
||||||
* types to use for some prototype. An optional associated object is used which
|
* types to use for some prototype. An optional associated object is used which
|
||||||
* allows multiple groups to be created with the same prototype. The
|
* allows multiple groups to be created with the same prototype. The
|
||||||
* associated object may be a function (for types constructed with 'new') or a
|
* associated object may be a function (for types constructed with 'new') or a
|
||||||
* type descriptor (for typed objects). These entries are also used for the set
|
* type descriptor (for typed objects). These entries are also used for the set
|
||||||
* of lazy groups in the compartment, which use a null associated object
|
* of lazy groups in the realm, which use a null associated object
|
||||||
* (though there are only a few of these per compartment).
|
* (though there are only a few of these per realm).
|
||||||
*/
|
*/
|
||||||
struct ObjectGroupCompartment::NewEntry
|
struct ObjectGroupRealm::NewEntry
|
||||||
{
|
{
|
||||||
ReadBarrieredObjectGroup group;
|
ReadBarrieredObjectGroup group;
|
||||||
|
|
||||||
|
@ -405,7 +406,7 @@ struct ObjectGroupCompartment::NewEntry
|
||||||
return mozilla::AddToHash(hash, mozilla::HashGeneric(lookup.clasp));
|
return mozilla::AddToHash(hash, mozilla::HashGeneric(lookup.clasp));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool match(const ObjectGroupCompartment::NewEntry& key, const Lookup& lookup) {
|
static inline bool match(const ObjectGroupRealm::NewEntry& key, const Lookup& lookup) {
|
||||||
if (lookup.clasp && key.group.unbarrieredGet()->clasp() != lookup.clasp)
|
if (lookup.clasp && key.group.unbarrieredGet()->clasp() != lookup.clasp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -430,18 +431,18 @@ struct ObjectGroupCompartment::NewEntry
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
template <>
|
template <>
|
||||||
struct FallibleHashMethods<ObjectGroupCompartment::NewEntry>
|
struct FallibleHashMethods<ObjectGroupRealm::NewEntry>
|
||||||
{
|
{
|
||||||
template <typename Lookup> static bool hasHash(Lookup&& l) {
|
template <typename Lookup> static bool hasHash(Lookup&& l) {
|
||||||
return ObjectGroupCompartment::NewEntry::hasHash(mozilla::Forward<Lookup>(l));
|
return ObjectGroupRealm::NewEntry::hasHash(mozilla::Forward<Lookup>(l));
|
||||||
}
|
}
|
||||||
template <typename Lookup> static bool ensureHash(Lookup&& l) {
|
template <typename Lookup> static bool ensureHash(Lookup&& l) {
|
||||||
return ObjectGroupCompartment::NewEntry::ensureHash(mozilla::Forward<Lookup>(l));
|
return ObjectGroupRealm::NewEntry::ensureHash(mozilla::Forward<Lookup>(l));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
class ObjectGroupCompartment::NewTable : public JS::WeakCache<js::GCHashSet<NewEntry, NewEntry,
|
class ObjectGroupRealm::NewTable : public JS::WeakCache<js::GCHashSet<NewEntry, NewEntry,
|
||||||
SystemAllocPolicy>>
|
SystemAllocPolicy>>
|
||||||
{
|
{
|
||||||
using Table = js::GCHashSet<NewEntry, NewEntry, SystemAllocPolicy>;
|
using Table = js::GCHashSet<NewEntry, NewEntry, SystemAllocPolicy>;
|
||||||
|
@ -451,8 +452,20 @@ class ObjectGroupCompartment::NewTable : public JS::WeakCache<js::GCHashSet<NewE
|
||||||
explicit NewTable(Zone* zone) : Base(zone) {}
|
explicit NewTable(Zone* zone) : Base(zone) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* static*/ ObjectGroupRealm&
|
||||||
|
ObjectGroupRealm::get(ObjectGroup* group)
|
||||||
|
{
|
||||||
|
return group->realm()->objectGroups_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static*/ ObjectGroupRealm&
|
||||||
|
ObjectGroupRealm::getForNewObject(JSContext* cx)
|
||||||
|
{
|
||||||
|
return cx->realm()->objectGroups_;
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE ObjectGroup*
|
MOZ_ALWAYS_INLINE ObjectGroup*
|
||||||
ObjectGroupCompartment::DefaultNewGroupCache::lookup(const Class* clasp, TaggedProto proto,
|
ObjectGroupRealm::DefaultNewGroupCache::lookup(const Class* clasp, TaggedProto proto,
|
||||||
JSObject* associated)
|
JSObject* associated)
|
||||||
{
|
{
|
||||||
if (group_ &&
|
if (group_ &&
|
||||||
|
@ -503,17 +516,17 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
||||||
clasp = &PlainObject::class_;
|
clasp = &PlainObject::class_;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment& groups = cx->compartment()->objectGroups;
|
ObjectGroupRealm& groups = ObjectGroupRealm::getForNewObject(cx);
|
||||||
|
|
||||||
if (ObjectGroup* group = groups.defaultNewGroupCache.lookup(clasp, proto, associated))
|
if (ObjectGroup* group = groups.defaultNewGroupCache.lookup(clasp, proto, associated))
|
||||||
return group;
|
return group;
|
||||||
|
|
||||||
AutoEnterAnalysis enter(cx);
|
AutoEnterAnalysis enter(cx);
|
||||||
|
|
||||||
ObjectGroupCompartment::NewTable*& table = groups.defaultNewTable;
|
ObjectGroupRealm::NewTable*& table = groups.defaultNewTable;
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
table = cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());
|
table = cx->new_<ObjectGroupRealm::NewTable>(cx->zone());
|
||||||
if (!table || !table->init()) {
|
if (!table || !table->init()) {
|
||||||
js_delete(table);
|
js_delete(table);
|
||||||
table = nullptr;
|
table = nullptr;
|
||||||
|
@ -545,8 +558,8 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::NewTable::AddPtr p =
|
ObjectGroupRealm::NewTable::AddPtr p =
|
||||||
table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated));
|
table->lookupForAdd(ObjectGroupRealm::NewEntry::Lookup(clasp, proto, associated));
|
||||||
if (p) {
|
if (p) {
|
||||||
ObjectGroup* group = p->group;
|
ObjectGroup* group = p->group;
|
||||||
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
|
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
|
||||||
|
@ -562,12 +575,12 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
||||||
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
|
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
|
||||||
|
|
||||||
Rooted<TaggedProto> protoRoot(cx, proto);
|
Rooted<TaggedProto> protoRoot(cx, proto);
|
||||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
|
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
|
||||||
protoRoot, initialFlags);
|
protoRoot, initialFlags);
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated))) {
|
if (!table->add(p, ObjectGroupRealm::NewEntry(group, associated))) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -605,14 +618,15 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ ObjectGroup*
|
/* static */ ObjectGroup*
|
||||||
ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto proto)
|
ObjectGroup::lazySingletonGroup(JSContext* cx, ObjectGroupRealm& realm, const Class* clasp,
|
||||||
|
TaggedProto proto)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
|
MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
|
||||||
|
|
||||||
ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.lazyTable;
|
ObjectGroupRealm::NewTable*& table = realm.lazyTable;
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
table = cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());
|
table = cx->new_<ObjectGroupRealm::NewTable>(cx->zone());
|
||||||
if (!table || !table->init()) {
|
if (!table || !table->init()) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
js_delete(table);
|
js_delete(table);
|
||||||
|
@ -621,8 +635,8 @@ ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::NewTable::AddPtr p =
|
ObjectGroupRealm::NewTable::AddPtr p =
|
||||||
table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr));
|
table->lookupForAdd(ObjectGroupRealm::NewEntry::Lookup(clasp, proto, nullptr));
|
||||||
if (p) {
|
if (p) {
|
||||||
ObjectGroup* group = p->group;
|
ObjectGroup* group = p->group;
|
||||||
MOZ_ASSERT(group->lazy());
|
MOZ_ASSERT(group->lazy());
|
||||||
|
@ -634,12 +648,12 @@ ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto p
|
||||||
|
|
||||||
Rooted<TaggedProto> protoRoot(cx, proto);
|
Rooted<TaggedProto> protoRoot(cx, proto);
|
||||||
ObjectGroup* group =
|
ObjectGroup* group =
|
||||||
ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot,
|
ObjectGroupRealm::makeGroup(cx, clasp, protoRoot,
|
||||||
OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
|
OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr))) {
|
if (!table->add(p, ObjectGroupRealm::NewEntry(group, nullptr))) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -648,13 +662,14 @@ ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto p
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
ObjectGroup::setDefaultNewGroupUnknown(JSContext* cx, const Class* clasp, HandleObject obj)
|
ObjectGroup::setDefaultNewGroupUnknown(JSContext* cx, ObjectGroupRealm& realm, const Class* clasp,
|
||||||
|
HandleObject obj)
|
||||||
{
|
{
|
||||||
// If the object already has a new group, mark that group as unknown.
|
// If the object already has a new group, mark that group as unknown.
|
||||||
ObjectGroupCompartment::NewTable* table = cx->compartment()->objectGroups.defaultNewTable;
|
ObjectGroupRealm::NewTable* table = realm.defaultNewTable;
|
||||||
if (table) {
|
if (table) {
|
||||||
Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj));
|
Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj));
|
||||||
auto lookup = ObjectGroupCompartment::NewEntry::Lookup(clasp, taggedProto, nullptr);
|
auto lookup = ObjectGroupRealm::NewEntry::Lookup(clasp, taggedProto, nullptr);
|
||||||
auto p = table->lookup(lookup);
|
auto p = table->lookup(lookup);
|
||||||
if (p)
|
if (p)
|
||||||
MarkObjectGroupUnknownProperties(cx, p->group);
|
MarkObjectGroupUnknownProperties(cx, p->group);
|
||||||
|
@ -665,10 +680,10 @@ ObjectGroup::setDefaultNewGroupUnknown(JSContext* cx, const Class* clasp, Handle
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
ObjectGroup::hasDefaultNewGroup(JSObject* proto, const Class* clasp, ObjectGroup* group)
|
ObjectGroup::hasDefaultNewGroup(JSObject* proto, const Class* clasp, ObjectGroup* group)
|
||||||
{
|
{
|
||||||
ObjectGroupCompartment::NewTable* table = proto->compartment()->objectGroups.defaultNewTable;
|
ObjectGroupRealm::NewTable* table = ObjectGroupRealm::get(group).defaultNewTable;
|
||||||
|
|
||||||
if (table) {
|
if (table) {
|
||||||
auto lookup = ObjectGroupCompartment::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr);
|
auto lookup = ObjectGroupRealm::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr);
|
||||||
auto p = table->lookup(lookup);
|
auto p = table->lookup(lookup);
|
||||||
return p && p->group == group;
|
return p && p->group == group;
|
||||||
}
|
}
|
||||||
|
@ -735,10 +750,10 @@ ObjectGroup::defaultNewGroup(JSContext* cx, JSProtoKey key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// ObjectGroupCompartment ArrayObjectTable
|
// ObjectGroupRealm ArrayObjectTable
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher<ArrayObjectKey>
|
struct ObjectGroupRealm::ArrayObjectKey : public DefaultHasher<ArrayObjectKey>
|
||||||
{
|
{
|
||||||
TypeSet::Type type;
|
TypeSet::Type type;
|
||||||
|
|
||||||
|
@ -833,11 +848,11 @@ ObjectGroup::newArrayObject(JSContext* cx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::ArrayObjectTable*& table =
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
cx->compartment()->objectGroups.arrayObjectTable;
|
ObjectGroupRealm::ArrayObjectTable*& table = realm.arrayObjectTable;
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
table = cx->new_<ObjectGroupCompartment::ArrayObjectTable>();
|
table = cx->new_<ObjectGroupRealm::ArrayObjectTable>();
|
||||||
if (!table || !table->init()) {
|
if (!table || !table->init()) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
js_delete(table);
|
js_delete(table);
|
||||||
|
@ -846,8 +861,8 @@ ObjectGroup::newArrayObject(JSContext* cx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::ArrayObjectKey key(elementType);
|
ObjectGroupRealm::ArrayObjectKey key(elementType);
|
||||||
DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable> p(cx, *table, key);
|
DependentAddPtr<ObjectGroupRealm::ArrayObjectTable> p(cx, *table, key);
|
||||||
|
|
||||||
RootedObjectGroup group(cx);
|
RootedObjectGroup group(cx);
|
||||||
if (p) {
|
if (p) {
|
||||||
|
@ -857,13 +872,13 @@ ObjectGroup::newArrayObject(JSContext* cx,
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
|
Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
|
||||||
group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto);
|
group = ObjectGroupRealm::makeGroup(cx, &ArrayObject::class_, taggedProto);
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType);
|
AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType);
|
||||||
|
|
||||||
if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group))
|
if (!p.add(cx, *table, ObjectGroupRealm::ArrayObjectKey(elementType), group))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1041,10 +1056,10 @@ js::CombinePlainObjectPropertyTypes(JSContext* cx, JSObject* newObj,
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// ObjectGroupCompartment PlainObjectTable
|
// ObjectGroupRealm PlainObjectTable
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct ObjectGroupCompartment::PlainObjectKey
|
struct ObjectGroupRealm::PlainObjectKey
|
||||||
{
|
{
|
||||||
jsid* properties;
|
jsid* properties;
|
||||||
uint32_t nproperties;
|
uint32_t nproperties;
|
||||||
|
@ -1082,7 +1097,7 @@ struct ObjectGroupCompartment::PlainObjectKey
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ObjectGroupCompartment::PlainObjectEntry
|
struct ObjectGroupRealm::PlainObjectEntry
|
||||||
{
|
{
|
||||||
ReadBarrieredObjectGroup group;
|
ReadBarrieredObjectGroup group;
|
||||||
ReadBarrieredShape shape;
|
ReadBarrieredShape shape;
|
||||||
|
@ -1156,11 +1171,11 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
||||||
if (newKind == SingletonObject || nproperties == 0 || nproperties >= PropertyTree::MAX_HEIGHT)
|
if (newKind == SingletonObject || nproperties == 0 || nproperties >= PropertyTree::MAX_HEIGHT)
|
||||||
return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
|
return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
|
||||||
|
|
||||||
ObjectGroupCompartment::PlainObjectTable*& table =
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
cx->compartment()->objectGroups.plainObjectTable;
|
ObjectGroupRealm::PlainObjectTable*& table = realm.plainObjectTable;
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
table = cx->new_<ObjectGroupCompartment::PlainObjectTable>();
|
table = cx->new_<ObjectGroupRealm::PlainObjectTable>();
|
||||||
if (!table || !table->init()) {
|
if (!table || !table->init()) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
js_delete(table);
|
js_delete(table);
|
||||||
|
@ -1169,8 +1184,8 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties);
|
ObjectGroupRealm::PlainObjectKey::Lookup lookup(properties, nproperties);
|
||||||
ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookup(lookup);
|
ObjectGroupRealm::PlainObjectTable::Ptr p = table->lookup(lookup);
|
||||||
|
|
||||||
if (!p) {
|
if (!p) {
|
||||||
if (!CanShareObjectGroup(properties, nproperties))
|
if (!CanShareObjectGroup(properties, nproperties))
|
||||||
|
@ -1181,8 +1196,8 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
||||||
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_,
|
RootedObjectGroup group(cx, ObjectGroupRealm::makeGroup(cx, &PlainObject::class_,
|
||||||
tagged));
|
tagged));
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -1234,17 +1249,17 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
||||||
AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
|
AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::PlainObjectKey key;
|
ObjectGroupRealm::PlainObjectKey key;
|
||||||
key.properties = ids;
|
key.properties = ids;
|
||||||
key.nproperties = nproperties;
|
key.nproperties = nproperties;
|
||||||
MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
|
MOZ_ASSERT(ObjectGroupRealm::PlainObjectKey::match(key, lookup));
|
||||||
|
|
||||||
ObjectGroupCompartment::PlainObjectEntry entry;
|
ObjectGroupRealm::PlainObjectEntry entry;
|
||||||
entry.group.set(group);
|
entry.group.set(group);
|
||||||
entry.shape.set(obj->lastProperty());
|
entry.shape.set(obj->lastProperty());
|
||||||
entry.types = types;
|
entry.types = types;
|
||||||
|
|
||||||
ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
|
ObjectGroupRealm::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
|
||||||
if (!table->add(np, key, entry)) {
|
if (!table->add(np, key, entry)) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1323,10 +1338,10 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// ObjectGroupCompartment AllocationSiteTable
|
// ObjectGroupRealm AllocationSiteTable
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
|
struct ObjectGroupRealm::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
|
||||||
ReadBarrieredScript script;
|
ReadBarrieredScript script;
|
||||||
|
|
||||||
uint32_t offset : 24;
|
uint32_t offset : 24;
|
||||||
|
@ -1394,7 +1409,7 @@ struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<Allocati
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectGroupCompartment::AllocationSiteTable
|
class ObjectGroupRealm::AllocationSiteTable
|
||||||
: public JS::WeakCache<js::GCHashMap<AllocationSiteKey, ReadBarrieredObjectGroup,
|
: public JS::WeakCache<js::GCHashMap<AllocationSiteKey, ReadBarrieredObjectGroup,
|
||||||
AllocationSiteKey, SystemAllocPolicy>>
|
AllocationSiteKey, SystemAllocPolicy>>
|
||||||
{
|
{
|
||||||
|
@ -1412,20 +1427,21 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
|
MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
|
||||||
MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
|
MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
|
||||||
|
MOZ_ASSERT(cx->realm() == scriptArg->realm());
|
||||||
|
|
||||||
uint32_t offset = scriptArg->pcToOffset(pc);
|
uint32_t offset = scriptArg->pcToOffset(pc);
|
||||||
|
|
||||||
if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) {
|
if (offset >= ObjectGroupRealm::AllocationSiteKey::OFFSET_LIMIT) {
|
||||||
if (protoArg)
|
if (protoArg)
|
||||||
return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg));
|
return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg));
|
||||||
return defaultNewGroup(cx, kind);
|
return defaultNewGroup(cx, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGroupCompartment::AllocationSiteTable*& table =
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
cx->compartment()->objectGroups.allocationSiteTable;
|
ObjectGroupRealm::AllocationSiteTable*& table = realm.allocationSiteTable;
|
||||||
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
table = cx->new_<ObjectGroupCompartment::AllocationSiteTable>(cx->zone());
|
table = cx->new_<ObjectGroupRealm::AllocationSiteTable>(cx->zone());
|
||||||
if (!table || !table->init()) {
|
if (!table || !table->init()) {
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
js_delete(table);
|
js_delete(table);
|
||||||
|
@ -1442,18 +1458,18 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rooted<ObjectGroupCompartment::AllocationSiteKey> key(cx,
|
Rooted<ObjectGroupRealm::AllocationSiteKey> key(cx,
|
||||||
ObjectGroupCompartment::AllocationSiteKey(script, offset, kind, proto));
|
ObjectGroupRealm::AllocationSiteKey(script, offset, kind, proto));
|
||||||
|
|
||||||
ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key);
|
ObjectGroupRealm::AllocationSiteTable::AddPtr p = table->lookupForAdd(key);
|
||||||
if (p)
|
if (p)
|
||||||
return p->value();
|
return p->value();
|
||||||
|
|
||||||
AutoEnterAnalysis enter(cx);
|
AutoEnterAnalysis enter(cx);
|
||||||
|
|
||||||
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
||||||
ObjectGroup* res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged,
|
ObjectGroup* res = ObjectGroupRealm::makeGroup(cx, GetClassForProtoKey(kind), tagged,
|
||||||
OBJECT_FLAG_FROM_ALLOCATION_SITE);
|
OBJECT_FLAG_FROM_ALLOCATION_SITE);
|
||||||
if (!res)
|
if (!res)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -1480,9 +1496,11 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
ObjectGroupRealm::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
||||||
JSProtoKey kind, ObjectGroup* group)
|
JSProtoKey kind, ObjectGroup* group)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(script->realm() == group->realm());
|
||||||
|
|
||||||
AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull());
|
AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull());
|
||||||
|
|
||||||
AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key);
|
AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key);
|
||||||
|
@ -1590,13 +1608,13 @@ ObjectGroup::findAllocationSite(JSContext* cx, ObjectGroup* group,
|
||||||
*script = nullptr;
|
*script = nullptr;
|
||||||
*offset = 0;
|
*offset = 0;
|
||||||
|
|
||||||
const ObjectGroupCompartment::AllocationSiteTable* table =
|
ObjectGroupRealm& realm = ObjectGroupRealm::get(group);
|
||||||
cx->compartment()->objectGroups.allocationSiteTable;
|
const ObjectGroupRealm::AllocationSiteTable* table = realm.allocationSiteTable;
|
||||||
|
|
||||||
if (!table)
|
if (!table)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (ObjectGroupCompartment::AllocationSiteTable::Range r = table->all();
|
for (ObjectGroupRealm::AllocationSiteTable::Range r = table->all();
|
||||||
!r.empty();
|
!r.empty();
|
||||||
r.popFront())
|
r.popFront())
|
||||||
{
|
{
|
||||||
|
@ -1611,10 +1629,10 @@ ObjectGroup::findAllocationSite(JSContext* cx, ObjectGroup* group,
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// ObjectGroupCompartment
|
// ObjectGroupRealm
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
ObjectGroupCompartment::~ObjectGroupCompartment()
|
ObjectGroupRealm::~ObjectGroupRealm()
|
||||||
{
|
{
|
||||||
js_delete(defaultNewTable);
|
js_delete(defaultNewTable);
|
||||||
js_delete(lazyTable);
|
js_delete(lazyTable);
|
||||||
|
@ -1625,7 +1643,7 @@ ObjectGroupCompartment::~ObjectGroupCompartment()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::removeDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
ObjectGroupRealm::removeDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
||||||
JSObject* associated)
|
JSObject* associated)
|
||||||
{
|
{
|
||||||
auto p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated));
|
auto p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated));
|
||||||
|
@ -1636,7 +1654,7 @@ ObjectGroupCompartment::removeDefaultNewGroup(const Class* clasp, TaggedProto pr
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::replaceDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
ObjectGroupRealm::replaceDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
||||||
JSObject* associated, ObjectGroup* group)
|
JSObject* associated, ObjectGroup* group)
|
||||||
{
|
{
|
||||||
NewEntry::Lookup lookup(clasp, proto, associated);
|
NewEntry::Lookup lookup(clasp, proto, associated);
|
||||||
|
@ -1654,9 +1672,9 @@ ObjectGroupCompartment::replaceDefaultNewGroup(const Class* clasp, TaggedProto p
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
ObjectGroup*
|
ObjectGroup*
|
||||||
ObjectGroupCompartment::makeGroup(JSContext* cx, const Class* clasp,
|
ObjectGroupRealm::makeGroup(JSContext* cx, const Class* clasp,
|
||||||
Handle<TaggedProto> proto,
|
Handle<TaggedProto> proto,
|
||||||
ObjectGroupFlags initialFlags /* = 0 */)
|
ObjectGroupFlags initialFlags /* = 0 */)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
|
MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
|
||||||
|
|
||||||
|
@ -1670,9 +1688,9 @@ ObjectGroupCompartment::makeGroup(JSContext* cx, const Class* clasp,
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
ObjectGroup*
|
ObjectGroup*
|
||||||
ObjectGroupCompartment::getStringSplitStringGroup(JSContext* cx)
|
ObjectGroupRealm::getStringSplitStringGroup(JSContext* cx)
|
||||||
{
|
{
|
||||||
ObjectGroupCompartment& groups = cx->compartment()->objectGroups;
|
ObjectGroupRealm& groups = ObjectGroupRealm::getForNewObject(cx);
|
||||||
|
|
||||||
ObjectGroup* group = groups.stringSplitStringGroup.get();
|
ObjectGroup* group = groups.stringSplitStringGroup.get();
|
||||||
if (group) {
|
if (group) {
|
||||||
|
@ -1698,11 +1716,11 @@ ObjectGroupCompartment::getStringSplitStringGroup(JSContext* cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
ObjectGroupRealm::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||||
size_t* allocationSiteTables,
|
size_t* allocationSiteTables,
|
||||||
size_t* arrayObjectGroupTables,
|
size_t* arrayObjectGroupTables,
|
||||||
size_t* plainObjectGroupTables,
|
size_t* plainObjectGroupTables,
|
||||||
size_t* compartmentTables)
|
size_t* realmTables)
|
||||||
{
|
{
|
||||||
if (allocationSiteTable)
|
if (allocationSiteTable)
|
||||||
*allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
|
*allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
|
||||||
|
@ -1726,14 +1744,14 @@ ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultNewTable)
|
if (defaultNewTable)
|
||||||
*compartmentTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf);
|
*realmTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf);
|
||||||
|
|
||||||
if (lazyTable)
|
if (lazyTable)
|
||||||
*compartmentTables += lazyTable->sizeOfIncludingThis(mallocSizeOf);
|
*realmTables += lazyTable->sizeOfIncludingThis(mallocSizeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::clearTables()
|
ObjectGroupRealm::clearTables()
|
||||||
{
|
{
|
||||||
if (allocationSiteTable && allocationSiteTable->initialized())
|
if (allocationSiteTable && allocationSiteTable->initialized())
|
||||||
allocationSiteTable->clear();
|
allocationSiteTable->clear();
|
||||||
|
@ -1756,8 +1774,8 @@ ObjectGroupCompartment::clearTables()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
ObjectGroupCompartment::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey* key,
|
ObjectGroupRealm::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey* key,
|
||||||
PlainObjectEntry* entry)
|
PlainObjectEntry* entry)
|
||||||
{
|
{
|
||||||
if (!(JS::GCPolicy<PlainObjectKey>::needsSweep(key) || entry->needsSweep(key->nproperties)))
|
if (!(JS::GCPolicy<PlainObjectKey>::needsSweep(key) || entry->needsSweep(key->nproperties)))
|
||||||
return false;
|
return false;
|
||||||
|
@ -1767,7 +1785,7 @@ ObjectGroupCompartment::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey*
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::sweep()
|
ObjectGroupRealm::sweep()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Iterate through the array/object group tables and remove all entries
|
* Iterate through the array/object group tables and remove all entries
|
||||||
|
@ -1786,7 +1804,7 @@ ObjectGroupCompartment::sweep()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
|
ObjectGroupRealm::fixupNewTableAfterMovingGC(NewTable* table)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Each entry's hash depends on the object's prototype and we can't tell
|
* Each entry's hash depends on the object's prototype and we can't tell
|
||||||
|
@ -1817,7 +1835,7 @@ ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
|
||||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||||
|
|
||||||
void
|
void
|
||||||
ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable* table)
|
ObjectGroupRealm::checkNewTableAfterMovingGC(NewTable* table)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Assert that nothing points into the nursery or needs to be relocated, and
|
* Assert that nothing points into the nursery or needs to be relocated, and
|
||||||
|
|
|
@ -29,6 +29,7 @@ class HeapTypeSet;
|
||||||
class AutoClearTypeInferenceStateOnOOM;
|
class AutoClearTypeInferenceStateOnOOM;
|
||||||
class AutoSweepObjectGroup;
|
class AutoSweepObjectGroup;
|
||||||
class CompilerConstraintList;
|
class CompilerConstraintList;
|
||||||
|
class ObjectGroupRealm;
|
||||||
|
|
||||||
namespace gc {
|
namespace gc {
|
||||||
void MergeCompartments(JSCompartment* source, JSCompartment* target);
|
void MergeCompartments(JSCompartment* source, JSCompartment* target);
|
||||||
|
@ -527,21 +528,22 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
static bool useSingletonForAllocationSite(JSScript* script, jsbytecode* pc,
|
static bool useSingletonForAllocationSite(JSScript* script, jsbytecode* pc,
|
||||||
const Class* clasp);
|
const Class* clasp);
|
||||||
|
|
||||||
// Static accessors for ObjectGroupCompartment NewTable.
|
// Static accessors for ObjectGroupRealm NewTable.
|
||||||
|
|
||||||
static ObjectGroup* defaultNewGroup(JSContext* cx, const Class* clasp,
|
static ObjectGroup* defaultNewGroup(JSContext* cx, const Class* clasp,
|
||||||
TaggedProto proto,
|
TaggedProto proto,
|
||||||
JSObject* associated = nullptr);
|
JSObject* associated = nullptr);
|
||||||
static ObjectGroup* lazySingletonGroup(JSContext* cx, const Class* clasp,
|
static ObjectGroup* lazySingletonGroup(JSContext* cx, ObjectGroupRealm& realm,
|
||||||
TaggedProto proto);
|
const Class* clasp, TaggedProto proto);
|
||||||
|
|
||||||
static void setDefaultNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj);
|
static void setDefaultNewGroupUnknown(JSContext* cx, ObjectGroupRealm& realm,
|
||||||
|
const js::Class* clasp, JS::HandleObject obj);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static bool hasDefaultNewGroup(JSObject* proto, const Class* clasp, ObjectGroup* group);
|
static bool hasDefaultNewGroup(JSObject* proto, const Class* clasp, ObjectGroup* group);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable.
|
// Static accessors for ObjectGroupRealm ArrayObjectTable and PlainObjectTable.
|
||||||
|
|
||||||
enum class NewArrayKind {
|
enum class NewArrayKind {
|
||||||
Normal, // Specialize array group based on its element type.
|
Normal, // Specialize array group based on its element type.
|
||||||
|
@ -561,7 +563,7 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
IdValuePair* properties, size_t nproperties,
|
IdValuePair* properties, size_t nproperties,
|
||||||
NewObjectKind newKind);
|
NewObjectKind newKind);
|
||||||
|
|
||||||
// Static accessors for ObjectGroupCompartment AllocationSiteTable.
|
// Static accessors for ObjectGroupRealm AllocationSiteTable.
|
||||||
|
|
||||||
// Get a non-singleton group to use for objects created at the specified
|
// Get a non-singleton group to use for objects created at the specified
|
||||||
// allocation site.
|
// allocation site.
|
||||||
|
@ -589,8 +591,8 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key);
|
static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structure used to manage the groups in a compartment.
|
// Structure used to manage the groups in a realm.
|
||||||
class ObjectGroupCompartment
|
class ObjectGroupRealm
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
class NewTable;
|
class NewTable;
|
||||||
|
@ -615,7 +617,7 @@ class ObjectGroupCompartment
|
||||||
class AllocationSiteTable;
|
class AllocationSiteTable;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Set of default 'new' or lazy groups in the compartment.
|
// Set of default 'new' or lazy groups in the realm.
|
||||||
NewTable* defaultNewTable = nullptr;
|
NewTable* defaultNewTable = nullptr;
|
||||||
NewTable* lazyTable = nullptr;
|
NewTable* lazyTable = nullptr;
|
||||||
|
|
||||||
|
@ -655,7 +657,7 @@ class ObjectGroupCompartment
|
||||||
// Table for referencing types of objects keyed to an allocation site.
|
// Table for referencing types of objects keyed to an allocation site.
|
||||||
AllocationSiteTable* allocationSiteTable = nullptr;
|
AllocationSiteTable* allocationSiteTable = nullptr;
|
||||||
|
|
||||||
// A single per-compartment ObjectGroup for all calls to StringSplitString.
|
// A single per-realm ObjectGroup for all calls to StringSplitString.
|
||||||
// StringSplitString is always called from self-hosted code, and conceptually
|
// StringSplitString is always called from self-hosted code, and conceptually
|
||||||
// the return object for a string.split(string) operation should have a
|
// the return object for a string.split(string) operation should have a
|
||||||
// unified type. Having a global group for this also allows us to remove
|
// unified type. Having a global group for this also allows us to remove
|
||||||
|
@ -663,6 +665,10 @@ class ObjectGroupCompartment
|
||||||
// on the basis of call-site pc.
|
// on the basis of call-site pc.
|
||||||
ReadBarrieredObjectGroup stringSplitStringGroup = {};
|
ReadBarrieredObjectGroup stringSplitStringGroup = {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// All unboxed layouts in the realm.
|
||||||
|
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
|
||||||
|
|
||||||
// END OF PROPERTIES
|
// END OF PROPERTIES
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -673,8 +679,14 @@ class ObjectGroupCompartment
|
||||||
public:
|
public:
|
||||||
struct NewEntry;
|
struct NewEntry;
|
||||||
|
|
||||||
ObjectGroupCompartment() = default;
|
ObjectGroupRealm() = default;
|
||||||
~ObjectGroupCompartment();
|
~ObjectGroupRealm();
|
||||||
|
|
||||||
|
ObjectGroupRealm(ObjectGroupRealm&) = delete;
|
||||||
|
void operator=(ObjectGroupRealm&) = delete;
|
||||||
|
|
||||||
|
static ObjectGroupRealm& get(ObjectGroup* group);
|
||||||
|
static ObjectGroupRealm& getForNewObject(JSContext* cx);
|
||||||
|
|
||||||
void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
||||||
JSProtoKey kind, ObjectGroup* group);
|
JSProtoKey kind, ObjectGroup* group);
|
||||||
|
@ -693,7 +705,7 @@ class ObjectGroupCompartment
|
||||||
size_t* allocationSiteTables,
|
size_t* allocationSiteTables,
|
||||||
size_t* arrayGroupTables,
|
size_t* arrayGroupTables,
|
||||||
size_t* plainObjectGroupTables,
|
size_t* plainObjectGroupTables,
|
||||||
size_t* compartmentTables);
|
size_t* realmTables);
|
||||||
|
|
||||||
void clearTables();
|
void clearTables();
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,9 @@ ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue pri
|
||||||
* ways.
|
* ways.
|
||||||
*/
|
*/
|
||||||
if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
|
if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
|
||||||
|
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||||
RootedObject protoObj(cx, proto.toObject());
|
RootedObject protoObj(cx, proto.toObject());
|
||||||
if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
|
if (!JSObject::setNewGroupUnknown(cx, realm, clasp, protoObj))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1208,16 +1208,16 @@ RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RegExpCompartment */
|
/* RegExpRealm */
|
||||||
|
|
||||||
RegExpCompartment::RegExpCompartment()
|
RegExpRealm::RegExpRealm()
|
||||||
: matchResultTemplateObject_(nullptr),
|
: matchResultTemplateObject_(nullptr),
|
||||||
optimizableRegExpPrototypeShape_(nullptr),
|
optimizableRegExpPrototypeShape_(nullptr),
|
||||||
optimizableRegExpInstanceShape_(nullptr)
|
optimizableRegExpInstanceShape_(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
ArrayObject*
|
ArrayObject*
|
||||||
RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
|
RegExpRealm::createMatchResultTemplateObject(JSContext* cx)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!matchResultTemplateObject_);
|
MOZ_ASSERT(!matchResultTemplateObject_);
|
||||||
|
|
||||||
|
@ -1229,7 +1229,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
|
||||||
|
|
||||||
// Create a new group for the template.
|
// Create a new group for the template.
|
||||||
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
||||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
|
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, templateObject->getClass(), proto);
|
||||||
if (!group)
|
if (!group)
|
||||||
return matchResultTemplateObject_; // = nullptr
|
return matchResultTemplateObject_; // = nullptr
|
||||||
templateObject->setGroup(group);
|
templateObject->setGroup(group);
|
||||||
|
@ -1274,7 +1274,7 @@ RegExpZone::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RegExpCompartment::sweep()
|
RegExpRealm::sweep()
|
||||||
{
|
{
|
||||||
if (matchResultTemplateObject_ &&
|
if (matchResultTemplateObject_ &&
|
||||||
IsAboutToBeFinalized(&matchResultTemplateObject_))
|
IsAboutToBeFinalized(&matchResultTemplateObject_))
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
class ArrayObject;
|
class ArrayObject;
|
||||||
class RegExpCompartment;
|
class RegExpRealm;
|
||||||
class RegExpShared;
|
class RegExpShared;
|
||||||
class RegExpStatics;
|
class RegExpStatics;
|
||||||
class VectorMatchPairs;
|
class VectorMatchPairs;
|
||||||
|
@ -288,7 +288,7 @@ class RegExpZone
|
||||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegExpCompartment
|
class RegExpRealm
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This is the template object where the result of re.exec() is based on,
|
* This is the template object where the result of re.exec() is based on,
|
||||||
|
@ -321,7 +321,7 @@ class RegExpCompartment
|
||||||
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
|
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RegExpCompartment();
|
explicit RegExpRealm();
|
||||||
|
|
||||||
void sweep();
|
void sweep();
|
||||||
|
|
||||||
|
@ -346,10 +346,10 @@ class RegExpCompartment
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t offsetOfOptimizableRegExpPrototypeShape() {
|
static size_t offsetOfOptimizableRegExpPrototypeShape() {
|
||||||
return offsetof(RegExpCompartment, optimizableRegExpPrototypeShape_);
|
return offsetof(RegExpRealm, optimizableRegExpPrototypeShape_);
|
||||||
}
|
}
|
||||||
static size_t offsetOfOptimizableRegExpInstanceShape() {
|
static size_t offsetOfOptimizableRegExpInstanceShape() {
|
||||||
return offsetof(RegExpCompartment, optimizableRegExpInstanceShape_);
|
return offsetof(RegExpRealm, optimizableRegExpInstanceShape_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,7 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
gc.atomsZone = atomsZone.get();
|
gc.atomsZone = atomsZone.get();
|
||||||
if (!atomsZone->compartments().append(atomsRealm.get()))
|
if (!atomsZone->compartments().append(JS::GetCompartmentForRealm(atomsRealm.get())))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
atomsRealm->setIsSystem(true);
|
atomsRealm->setIsSystem(true);
|
||||||
|
|
|
@ -101,21 +101,21 @@ LiveSavedFrameCache::find(JSContext* cx, FramePtr& framePtr, const jsbytecode* p
|
||||||
MOZ_ASSERT(initialized());
|
MOZ_ASSERT(initialized());
|
||||||
MOZ_ASSERT(framePtr.hasCachedSavedFrame());
|
MOZ_ASSERT(framePtr.hasCachedSavedFrame());
|
||||||
|
|
||||||
// If we flushed the cache due to a compartment mismatch, then we shouldn't
|
// If we flushed the cache due to a realm mismatch, then we shouldn't
|
||||||
// expect to find any frames in the cache.
|
// expect to find any frames in the cache.
|
||||||
if (frames->empty()) {
|
if (frames->empty()) {
|
||||||
frame.set(nullptr);
|
frame.set(nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All our SavedFrames should be in the same compartment. If the last
|
// All our SavedFrames should be in the same realm. If the last
|
||||||
// entry's SavedFrame's compartment doesn't match cx's, flush the cache.
|
// entry's SavedFrame's realm doesn't match cx's, flush the cache.
|
||||||
if (frames->back().savedFrame->compartment() != cx->compartment()) {
|
if (frames->back().savedFrame->realm() != cx->realm()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Check that they are, indeed, all in the same compartment.
|
// Check that they are, indeed, all in the same realm.
|
||||||
auto compartment = frames->back().savedFrame->compartment();
|
auto compartment = frames->back().savedFrame->realm();
|
||||||
for (const auto& f : (*frames))
|
for (const auto& f : (*frames))
|
||||||
MOZ_ASSERT(compartment == f.savedFrame->compartment());
|
MOZ_ASSERT(compartment == f.savedFrame->realm());
|
||||||
#endif
|
#endif
|
||||||
frames->clear();
|
frames->clear();
|
||||||
frame.set(nullptr);
|
frame.set(nullptr);
|
||||||
|
@ -124,7 +124,7 @@ LiveSavedFrameCache::find(JSContext* cx, FramePtr& framePtr, const jsbytecode* p
|
||||||
|
|
||||||
Key key(framePtr);
|
Key key(framePtr);
|
||||||
while (key != frames->back().key) {
|
while (key != frames->back().key) {
|
||||||
MOZ_ASSERT(frames->back().savedFrame->compartment() == cx->compartment());
|
MOZ_ASSERT(frames->back().savedFrame->realm() == cx->realm());
|
||||||
|
|
||||||
// We know that the cache does contain an entry for frameIter's frame,
|
// We know that the cache does contain an entry for frameIter's frame,
|
||||||
// since its bit is set. That entry must be below this one in the stack,
|
// since its bit is set. That entry must be below this one in the stack,
|
||||||
|
@ -559,7 +559,7 @@ SavedFrame::create(JSContext* cx)
|
||||||
// Ensure that we don't try to capture the stack again in the
|
// Ensure that we don't try to capture the stack again in the
|
||||||
// `SavedStacksMetadataBuilder` for this new SavedFrame object, and
|
// `SavedStacksMetadataBuilder` for this new SavedFrame object, and
|
||||||
// accidentally cause O(n^2) behavior.
|
// accidentally cause O(n^2) behavior.
|
||||||
SavedStacks::AutoReentrancyGuard guard(cx->compartment()->savedStacks());
|
SavedStacks::AutoReentrancyGuard guard(cx->realm()->savedStacks());
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
|
RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
|
||||||
if (!proto)
|
if (!proto)
|
||||||
|
@ -829,7 +829,7 @@ GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
{
|
{
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
|
@ -852,7 +852,7 @@ GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
MOZ_ASSERT(linep);
|
MOZ_ASSERT(linep);
|
||||||
|
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
|
@ -872,7 +872,7 @@ GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
MOZ_ASSERT(columnp);
|
MOZ_ASSERT(columnp);
|
||||||
|
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
|
@ -892,7 +892,7 @@ GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, Mutable
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
{
|
{
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
|
@ -915,7 +915,7 @@ GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleStr
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
{
|
{
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
|
@ -946,7 +946,7 @@ GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleOb
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
bool skippedAsync;
|
bool skippedAsync;
|
||||||
|
@ -979,7 +979,7 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||||
bool skippedAsync;
|
bool skippedAsync;
|
||||||
|
@ -1085,7 +1085,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
|
||||||
{
|
{
|
||||||
js::AssertHeapIsIdle();
|
js::AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
|
|
||||||
js::StringBuffer sb(cx);
|
js::StringBuffer sb(cx);
|
||||||
|
|
||||||
|
@ -1279,8 +1279,8 @@ SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
|
||||||
JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
|
JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(initialized());
|
MOZ_ASSERT(initialized());
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
assertSameCompartment(cx, this);
|
MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
|
||||||
|
|
||||||
if (creatingSavedFrame ||
|
if (creatingSavedFrame ||
|
||||||
cx->isExceptionPending() ||
|
cx->isExceptionPending() ||
|
||||||
|
@ -1301,8 +1301,8 @@ SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString
|
||||||
const Maybe<size_t>& maxFrameCount)
|
const Maybe<size_t>& maxFrameCount)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(initialized());
|
MOZ_ASSERT(initialized());
|
||||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
MOZ_RELEASE_ASSERT(cx->realm());
|
||||||
assertSameCompartment(cx, this);
|
MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
|
||||||
|
|
||||||
RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
|
RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
|
||||||
if (!asyncCauseAtom)
|
if (!asyncCauseAtom)
|
||||||
|
@ -1475,7 +1475,7 @@ SavedStacks::insertFrames(JSContext* cx, MutableHandleSavedFrame frame,
|
||||||
Rooted<LocationValue> location(cx);
|
Rooted<LocationValue> location(cx);
|
||||||
{
|
{
|
||||||
AutoRealmUnchecked ar(cx, iter.compartment());
|
AutoRealmUnchecked ar(cx, iter.compartment());
|
||||||
if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
|
if (!cx->realm()->savedStacks().getLocation(cx, iter, &location))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1618,12 +1618,12 @@ SavedStacks::adoptAsyncStack(JSContext* cx, MutableHandleSavedFrame asyncStack,
|
||||||
// Attach the asyncCause to the youngest frame.
|
// Attach the asyncCause to the youngest frame.
|
||||||
stackChain[0]->asyncCause = asyncCause;
|
stackChain[0]->asyncCause = asyncCause;
|
||||||
|
|
||||||
// If we walked the entire stack, and it's in cx's compartment, we don't
|
// If we walked the entire stack, and it's in cx's realm, we don't
|
||||||
// need to rebuild the full chain again using the lookup objects - we can
|
// need to rebuild the full chain again using the lookup objects - we can
|
||||||
// just use the existing chain. Only the asyncCause on the youngest frame
|
// just use the existing chain. Only the asyncCause on the youngest frame
|
||||||
// needs to be changed.
|
// needs to be changed.
|
||||||
if (currentSavedFrame == nullptr &&
|
if (currentSavedFrame == nullptr &&
|
||||||
asyncStack->compartment() == cx->compartment())
|
asyncStack->realm() == cx->realm())
|
||||||
{
|
{
|
||||||
SavedFrame::HandleLookup lookup = stackChain[0];
|
SavedFrame::HandleLookup lookup = stackChain[0];
|
||||||
lookup->parent = asyncStack->getParent();
|
lookup->parent = asyncStack->getParent();
|
||||||
|
@ -1748,7 +1748,8 @@ SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
|
||||||
// compartment. Otherwise, we would get dead cross-compartment scripts in
|
// compartment. Otherwise, we would get dead cross-compartment scripts in
|
||||||
// the cache because our compartment's sweep method isn't called when their
|
// the cache because our compartment's sweep method isn't called when their
|
||||||
// compartment gets collected.
|
// compartment gets collected.
|
||||||
assertSameCompartment(cx, this, iter.compartment());
|
MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
|
||||||
|
assertSameCompartment(cx, iter.compartment());
|
||||||
|
|
||||||
// When we have a |JSScript| for this frame, use a potentially memoized
|
// When we have a |JSScript| for this frame, use a potentially memoized
|
||||||
// location from our PCLocationMap and copy it into |locationp|. When we do
|
// location from our PCLocationMap and copy it into |locationp|. When we do
|
||||||
|
@ -1806,9 +1807,8 @@ SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
|
SavedStacks::chooseSamplingProbability(Realm* realm)
|
||||||
{
|
{
|
||||||
Realm* realm = JS::GetRealmForCompartment(compartment);
|
|
||||||
GlobalObject* global = realm->maybeGlobal();
|
GlobalObject* global = realm->maybeGlobal();
|
||||||
if (!global)
|
if (!global)
|
||||||
return;
|
return;
|
||||||
|
@ -1850,7 +1850,7 @@ SavedStacks::MetadataBuilder::build(JSContext* cx, HandleObject target,
|
||||||
{
|
{
|
||||||
RootedObject obj(cx, target);
|
RootedObject obj(cx, target);
|
||||||
|
|
||||||
SavedStacks& stacks = cx->compartment()->savedStacks();
|
SavedStacks& stacks = cx->realm()->savedStacks();
|
||||||
if (!stacks.bernoulli.trial())
|
if (!stacks.bernoulli.trial())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -1867,18 +1867,6 @@ SavedStacks::MetadataBuilder::build(JSContext* cx, HandleObject target,
|
||||||
|
|
||||||
const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
|
const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
|
||||||
|
|
||||||
#ifdef JS_CRASH_DIAGNOSTICS
|
|
||||||
void
|
|
||||||
CompartmentChecker::check(SavedStacks* stacks)
|
|
||||||
{
|
|
||||||
if (&compartment->savedStacks() != stacks) {
|
|
||||||
printf("*** Compartment SavedStacks mismatch: %p vs. %p\n",
|
|
||||||
(void*) &compartment->savedStacks(), stacks);
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* JS_CRASH_DIAGNOSTICS */
|
|
||||||
|
|
||||||
/* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsSystem;
|
/* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsSystem;
|
||||||
/* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsNotSystem;
|
/* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsNotSystem;
|
||||||
|
|
||||||
|
@ -2001,7 +1989,7 @@ ConstructSavedFrameStackSlow(JSContext* cx, JS::ubi::StackFrame& frame,
|
||||||
for (size_t i = stackChain->length(); i != 0; i--) {
|
for (size_t i = stackChain->length(); i != 0; i--) {
|
||||||
SavedFrame::HandleLookup lookup = stackChain[i-1];
|
SavedFrame::HandleLookup lookup = stackChain[i-1];
|
||||||
lookup->parent = parentFrame;
|
lookup->parent = parentFrame;
|
||||||
parentFrame = cx->compartment()->savedStacks().getOrCreateSavedFrame(cx, lookup);
|
parentFrame = cx->realm()->savedStacks().getOrCreateSavedFrame(cx, lookup);
|
||||||
if (!parentFrame)
|
if (!parentFrame)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,7 @@ class SavedStacks {
|
||||||
void trace(JSTracer* trc);
|
void trace(JSTracer* trc);
|
||||||
uint32_t count();
|
uint32_t count();
|
||||||
void clear();
|
void clear();
|
||||||
void chooseSamplingProbability(JSCompartment*);
|
void chooseSamplingProbability(JS::Realm* realm);
|
||||||
|
|
||||||
// Set the sampling random number generator's state to |state0| and
|
// Set the sampling random number generator's state to |state0| and
|
||||||
// |state1|. One or the other must be non-zero. See the comments for
|
// |state1|. One or the other must be non-zero. See the comments for
|
||||||
|
|
|
@ -1764,7 +1764,7 @@ js::intrinsic_StringSplitString(JSContext* cx, unsigned argc, Value* vp)
|
||||||
RootedString string(cx, args[0].toString());
|
RootedString string(cx, args[0].toString());
|
||||||
RootedString sep(cx, args[1].toString());
|
RootedString sep(cx, args[1].toString());
|
||||||
|
|
||||||
RootedObjectGroup group(cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
|
RootedObjectGroup group(cx, ObjectGroupRealm::getStringSplitStringGroup(cx));
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1790,7 +1790,7 @@ intrinsic_StringSplitStringLimit(JSContext* cx, unsigned argc, Value* vp)
|
||||||
uint32_t limit = uint32_t(args[2].toNumber());
|
uint32_t limit = uint32_t(args[2].toNumber());
|
||||||
MOZ_ASSERT(limit > 0, "Zero limit case is already handled in self-hosted code.");
|
MOZ_ASSERT(limit > 0, "Zero limit case is already handled in self-hosted code.");
|
||||||
|
|
||||||
RootedObjectGroup group(cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
|
RootedObjectGroup group(cx, ObjectGroupRealm::getStringSplitStringGroup(cx));
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -2809,7 +2809,7 @@ JSRuntime::createSelfHostingGlobal(JSContext* cx)
|
||||||
&shgClassOps
|
&shgClassOps
|
||||||
};
|
};
|
||||||
|
|
||||||
AutoRealmUnchecked ar(cx, realm);
|
AutoRealmUnchecked ar(cx, compartment);
|
||||||
Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
|
Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
|
||||||
if (!shg)
|
if (!shg)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -385,8 +385,8 @@ InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc)
|
||||||
traceValues(trc, 0, nlivefixed);
|
traceValues(trc, 0, nlivefixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script->compartment()->debugEnvs)
|
if (auto* debugEnvs = script->realm()->debugEnvs())
|
||||||
script->compartment()->debugEnvs->traceLiveFrame(trc, this);
|
debugEnvs->traceLiveFrame(trc, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче