зеркало из 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
|
||||
// Accessible
|
||||
virtual a11y::role NativeRole() const override;
|
||||
virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
|
||||
virtual TableAccessible* AsTable() override { return this; }
|
||||
|
||||
// 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.
|
||||
*/
|
||||
virtual bool IsProbablyLayoutTable() { return false; }
|
||||
virtual bool IsProbablyLayoutTable();
|
||||
|
||||
/**
|
||||
* Convert the table to an Accessible*.
|
||||
|
|
|
@ -21,6 +21,7 @@ UNIFIED_SOURCES += [
|
|||
'ImageAccessible.cpp',
|
||||
'OuterDocAccessible.cpp',
|
||||
'RootAccessible.cpp',
|
||||
'TableAccessible.cpp',
|
||||
'TableCellAccessible.cpp',
|
||||
'TextLeafAccessible.cpp',
|
||||
]
|
||||
|
|
|
@ -860,244 +860,6 @@ HTMLTableAccessible::Description(nsString& aDescription)
|
|||
#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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -156,7 +156,6 @@ public:
|
|||
virtual void SelectRow(uint32_t aRowIdx) override;
|
||||
virtual void UnselectCol(uint32_t aColIdx) override;
|
||||
virtual void UnselectRow(uint32_t aRowIdx) override;
|
||||
virtual bool IsProbablyLayoutTable() override;
|
||||
virtual Accessible* AsAccessible() override { return this; }
|
||||
|
||||
// Accessible
|
||||
|
@ -199,15 +198,6 @@ protected:
|
|||
TableSelection aTarget,
|
||||
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
|
||||
nsString mLayoutHeuristic;
|
||||
#endif
|
||||
|
|
|
@ -14,106 +14,114 @@
|
|||
src="../attributes.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest() {
|
||||
// Attribute we're looking for
|
||||
var attr = {
|
||||
"layout-guess": "true"
|
||||
};
|
||||
function isLayoutTable(id) {
|
||||
testAttrs(id, { "layout-guess": "true" }, true);
|
||||
}
|
||||
function isDataTable(id) {
|
||||
testAbsentAttrs(id, { "layout-guess": "true" });
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
// table with role of grid
|
||||
testAbsentAttrs("table1", attr);
|
||||
isDataTable("table1");
|
||||
// table with role of grid and datatable="0"
|
||||
testAbsentAttrs("table1.1", attr);
|
||||
isDataTable("table1.1");
|
||||
|
||||
// table with landmark role
|
||||
testAbsentAttrs("table2", attr);
|
||||
isDataTable("table2");
|
||||
|
||||
// table with summary
|
||||
testAbsentAttrs("table3", attr);
|
||||
isDataTable("table3");
|
||||
|
||||
// table with caption
|
||||
testAbsentAttrs("table4", attr);
|
||||
isDataTable("table4");
|
||||
|
||||
// layout table with empty caption
|
||||
testAttrs("table4.2", attr, true);
|
||||
isLayoutTable("table4.2");
|
||||
|
||||
// table with thead element
|
||||
testAbsentAttrs("table5", attr);
|
||||
isDataTable("table5");
|
||||
|
||||
// table with tfoot element
|
||||
testAbsentAttrs("table5.1", attr);
|
||||
isDataTable("table5.1");
|
||||
|
||||
// table with colgroup or col elements
|
||||
testAbsentAttrs("table5.2", attr);
|
||||
testAbsentAttrs("table5.3", attr);
|
||||
isDataTable("table5.2");
|
||||
isDataTable("table5.3");
|
||||
|
||||
// table with th element
|
||||
testAbsentAttrs("table6", attr);
|
||||
isDataTable("table6");
|
||||
|
||||
// table with headers attribute
|
||||
testAbsentAttrs("table6.2", attr);
|
||||
isDataTable("table6.2");
|
||||
|
||||
// table with scope attribute
|
||||
testAbsentAttrs("table6.2.2", attr);
|
||||
isDataTable("table6.2.2");
|
||||
|
||||
// table with abbr attribute
|
||||
testAbsentAttrs("table6.2.3", attr);
|
||||
isDataTable("table6.2.3");
|
||||
|
||||
// table with abbr element
|
||||
testAbsentAttrs("table6.3", attr);
|
||||
isDataTable("table6.3");
|
||||
|
||||
// table with abbr element having empty text node
|
||||
testAbsentAttrs("table6.4", attr);
|
||||
isDataTable("table6.4");
|
||||
|
||||
// table with abbr element and non-empty text node
|
||||
testAttrs("table6.5", attr, true);
|
||||
isLayoutTable("table6.5");
|
||||
|
||||
// layout table with nested table
|
||||
testAttrs("table9", attr, true);
|
||||
isLayoutTable("table9");
|
||||
|
||||
// layout table with 1 column
|
||||
testAttrs("table10", attr, true);
|
||||
isLayoutTable("table10");
|
||||
|
||||
// layout table with 1 row
|
||||
testAttrs("table11", attr, true);
|
||||
isLayoutTable("table11");
|
||||
|
||||
// table with 5 columns
|
||||
testAbsentAttrs("table12", attr);
|
||||
isDataTable("table12");
|
||||
|
||||
// table with a bordered cell
|
||||
testAbsentAttrs("table13", attr);
|
||||
isDataTable("table13");
|
||||
|
||||
// table with alternating row background colors
|
||||
testAbsentAttrs("table14", attr);
|
||||
isDataTable("table14");
|
||||
|
||||
// table with 3 columns and 21 rows
|
||||
testAbsentAttrs("table15", attr);
|
||||
isDataTable("table15");
|
||||
|
||||
// layout table that has a 100% width
|
||||
testAttrs("table16", attr, true);
|
||||
isLayoutTable("table16");
|
||||
|
||||
// layout table that has a 95% width in pixels
|
||||
testAttrs("table17", attr, true);
|
||||
isLayoutTable("table17");
|
||||
|
||||
// layout table with less than 10 columns
|
||||
testAttrs("table18", attr, true);
|
||||
isLayoutTable("table18");
|
||||
|
||||
// layout table with embedded iframe
|
||||
testAttrs("table19", attr, true);
|
||||
isLayoutTable("table19");
|
||||
|
||||
// tree grid, no layout table
|
||||
testAbsentAttrs("table20", attr);
|
||||
isDataTable("table20");
|
||||
|
||||
// layout table containing nested data table (having data structures)
|
||||
testAttrs("table21", attr, true);
|
||||
testAttrs("table21.2", attr, true);
|
||||
testAttrs("table21.3", attr, true);
|
||||
testAttrs("table21.4", attr, true);
|
||||
testAttrs("table21.5", attr, true);
|
||||
testAttrs("table21.6", attr, true);
|
||||
isLayoutTable("table21");
|
||||
isLayoutTable("table21.2");
|
||||
isLayoutTable("table21.3");
|
||||
isLayoutTable("table21.4");
|
||||
isLayoutTable("table21.5");
|
||||
isLayoutTable("table21.6");
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
@ -501,5 +509,32 @@
|
|||
</tfoot>
|
||||
</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>
|
||||
</html>
|
||||
|
|
|
@ -213,21 +213,17 @@ OptionsPanel.prototype = {
|
|||
"tools-not-supported-label");
|
||||
let atleastOneToolNotSupported = false;
|
||||
|
||||
const toolbox = this.toolbox;
|
||||
|
||||
// Signal tool registering/unregistering globally (for the tools registered
|
||||
// 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.
|
||||
let onCheckboxClick = function(tool) {
|
||||
let onCheckboxClick = function(telemetry, tool) {
|
||||
// Set the kill switch pref boolean to true
|
||||
Services.prefs.setBoolPref(tool.visibilityswitch, this.checked);
|
||||
|
||||
if (!tool.isWebExtension) {
|
||||
gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", tool.id);
|
||||
// Record which tools were registered and unregistered.
|
||||
this.telemetry.keyedScalarSet("devtools.tool.registered",
|
||||
tool.id,
|
||||
this.checked);
|
||||
telemetry.keyedScalarSet("devtools.tool.registered", tool.id, this.checked);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -253,7 +249,8 @@ OptionsPanel.prototype = {
|
|||
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(checkboxSpanLabel);
|
||||
|
@ -287,7 +284,7 @@ OptionsPanel.prototype = {
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
||||
additionalToolsBox.appendChild(createToolCheckbox({
|
||||
|
|
|
@ -36,15 +36,10 @@ pref("devtools.inspector.enabled", true);
|
|||
pref("devtools.inspector.activeSidebar", "ruleview");
|
||||
pref("devtools.inspector.remote", false);
|
||||
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
// Show the 3 pane onboarding tooltip in the inspector
|
||||
pref("devtools.inspector.show-three-pane-tooltip", true);
|
||||
// Enable the 3 pane mode in the inspector
|
||||
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
|
||||
pref("devtools.inspector.show_pseudo_elements", false);
|
||||
|
|
|
@ -27,6 +27,7 @@ class Telemetry {
|
|||
this.scalarSet = this.scalarSet.bind(this);
|
||||
this.scalarAdd = this.scalarAdd.bind(this);
|
||||
this.keyedScalarAdd = this.keyedScalarAdd.bind(this);
|
||||
this.keyedScalarSet = this.keyedScalarSet.bind(this);
|
||||
this.recordEvent = this.recordEvent.bind(this);
|
||||
this.setEventRecordingEnabled = this.setEventRecordingEnabled.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.
|
||||
*
|
||||
|
|
|
@ -2039,7 +2039,7 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
}
|
||||
|
||||
nsDOMCSSAttributeDeclaration*
|
||||
Element::GetSMILOverrideStyle()
|
||||
Element::SMILOverrideStyle()
|
||||
{
|
||||
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>
|
||||
// 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
|
||||
// display: none subtree, we keep it clean.
|
||||
//
|
||||
|
|
|
@ -361,7 +361,7 @@ public:
|
|||
* Note: This method is analogous to the 'GetStyle' method in
|
||||
* nsGenericHTMLElement and nsStyledElement.
|
||||
*/
|
||||
nsDOMCSSAttributeDeclaration* GetSMILOverrideStyle();
|
||||
nsDOMCSSAttributeDeclaration* SMILOverrideStyle();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* created.
|
||||
|
|
|
@ -135,6 +135,10 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
|||
int32_t aType,
|
||||
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) {
|
||||
return OnImageIsAnimated(aRequest);
|
||||
}
|
||||
|
@ -144,14 +148,6 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
|||
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
|
||||
// a local copy.
|
||||
|
@ -629,12 +625,6 @@ nsImageLoadingContent::GetRequest(int32_t aRequestType,
|
|||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsImageLoadingContent::CurrentRequestHasSize()
|
||||
{
|
||||
return HaveSize(mCurrentRequest);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
|
||||
{
|
||||
|
|
|
@ -521,7 +521,8 @@ skip-if = toolkit == 'android' #bug 687032
|
|||
[test_bug698381.html]
|
||||
[test_bug698384.html]
|
||||
[test_bug704063.html]
|
||||
[test_bug704320.html]
|
||||
[test_bug704320-1.html]
|
||||
[test_bug704320-2.html]
|
||||
[test_bug704320_policyset.html]
|
||||
[test_bug704320_policyset2.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>
|
||||
<!--
|
||||
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</title>
|
||||
<title>Test for Bug 704320-Part2</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"/>
|
||||
|
@ -29,9 +30,7 @@ var generateURLArray = (function(from, to){
|
|||
];
|
||||
});
|
||||
|
||||
let testIframeUrls = [generateURLArray('http', 'http'),
|
||||
generateURLArray('https', 'https'),
|
||||
generateURLArray('http', 'https'),
|
||||
let testIframeUrls = [generateURLArray('http', 'https'),
|
||||
generateURLArray('https', 'http')];
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -39,13 +38,12 @@ let advance = function(testName) {
|
|||
testsGenerator[testName].next();
|
||||
};
|
||||
|
||||
let testNames = ['testframeone', 'testframetwo', 'testframethree',
|
||||
'testframefour'];
|
||||
let testNames = ['testframeone', 'testframetwo'];
|
||||
let isTestFinished = 0;
|
||||
|
||||
function checkTestsCompleted() {
|
||||
isTestFinished++;
|
||||
if (isTestFinished == 4) {
|
||||
if (isTestFinished == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
@ -82,15 +80,11 @@ for (i = 0; i < testIframeUrls.length; i++) {
|
|||
</head>
|
||||
|
||||
<body onload="testsGenerator[testNames[0]].next();
|
||||
testsGenerator[testNames[1]].next();
|
||||
testsGenerator[testNames[2]].next();
|
||||
testsGenerator[testNames[3]].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>
|
||||
<iframe id="testframethree"></iframe>
|
||||
<iframe id="testframefour"></iframe>
|
||||
</body>
|
|
@ -481,7 +481,9 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
|
||||
using nsINamed::GetName;
|
||||
#endif
|
||||
|
||||
// nsIAsyncShutdownBlocker interface
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsSize.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsImageFrame.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIIOService.h"
|
||||
|
@ -949,7 +950,7 @@ HTMLImageElement::InResponsiveMode()
|
|||
}
|
||||
|
||||
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.
|
||||
// Similarly for if there is no newly selected source.
|
||||
|
@ -957,32 +958,58 @@ HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSel
|
|||
return false;
|
||||
}
|
||||
bool equal = false;
|
||||
return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) && equal &&
|
||||
aSelectedDensity == mCurrentDensity;
|
||||
return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) && equal;
|
||||
}
|
||||
|
||||
nsresult
|
||||
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) {
|
||||
// In responsive mode we generally want to re-run the full
|
||||
// selection algorithm whenever starting a new load, per
|
||||
// spec. This also causes us to re-resolve the URI as appropriate.
|
||||
if (!UpdateResponsiveSource() && !aAlwaysLoad) {
|
||||
// In responsive mode we generally want to re-run the full selection
|
||||
// algorithm whenever starting a new load, per spec.
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
} else if (mResponsiveSelector) {
|
||||
currentDensity = mResponsiveSelector->GetSelectedImageDensity();
|
||||
}
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIURI> selectedSource;
|
||||
double currentDensity = 1.0; // default to 1.0 for the src attribute case
|
||||
if (mResponsiveSelector) {
|
||||
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
|
||||
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mResponsiveSelector->GetSelectedImageTriggeringPrincipal();
|
||||
selectedSource = url;
|
||||
currentDensity = mResponsiveSelector->GetSelectedImageDensity();
|
||||
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
|
||||
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource)) {
|
||||
UpdateDensityOnly();
|
||||
return NS_OK;
|
||||
}
|
||||
if (url) {
|
||||
|
@ -995,12 +1022,11 @@ HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
|
|||
CancelImageRequests(aNotify);
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
nsIDocument* doc = GetOurOwnerDoc();
|
||||
if (doc) {
|
||||
StringToURI(src, doc, getter_AddRefs(selectedSource));
|
||||
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
StringToURI(src, doc, getter_AddRefs(selectedSource));
|
||||
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource)) {
|
||||
UpdateDensityOnly();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have a srcset attribute or are in a <picture> element,
|
||||
|
|
|
@ -43,6 +43,11 @@ public:
|
|||
|
||||
virtual bool Draggable() const override;
|
||||
|
||||
ResponsiveImageSelector* GetResponsiveImageSelector()
|
||||
{
|
||||
return mResponsiveSelector.get();
|
||||
}
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
|
||||
|
||||
|
@ -325,8 +330,8 @@ protected:
|
|||
// only mode after Bug 1076583
|
||||
bool InResponsiveMode();
|
||||
|
||||
// True if the given URL and density equal the last URL and density that was loaded by this element.
|
||||
bool SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSelectedDensity);
|
||||
// True if the given URL equals the last URL that was loaded by this element.
|
||||
bool SelectedSourceMatchesLast(nsIURI* aSelectedSource);
|
||||
|
||||
// Resolve and load the current mResponsiveSelector (responsive mode) or src
|
||||
// attr image.
|
||||
|
|
|
@ -902,6 +902,10 @@ nsPluginInstanceOwner::RequestCommitOrCancel(bool aCommitted)
|
|||
bool
|
||||
nsPluginInstanceOwner::EnableIME(bool aEnable)
|
||||
{
|
||||
if (NS_WARN_IF(!mPluginFrame)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
|
||||
if (!widget) {
|
||||
widget = GetRootWidgetForPluginFrame(mPluginFrame);
|
||||
|
|
|
@ -107,17 +107,14 @@ nsresult
|
|||
nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue)
|
||||
{
|
||||
NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
|
||||
return mElement->GetSMILOverrideStyle()->SetSMILValue(mPropID, aValue);
|
||||
return mElement->SMILOverrideStyle()->SetSMILValue(mPropID, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILCSSProperty::ClearAnimValue()
|
||||
{
|
||||
// Put empty string in override style for our property
|
||||
nsDOMCSSAttributeDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
|
||||
if (overrideDecl) {
|
||||
overrideDecl->SetPropertyValue(mPropID, EmptyString(), nullptr);
|
||||
}
|
||||
mElement->SMILOverrideStyle()->SetPropertyValue(mPropID, EmptyString(), nullptr);
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
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,
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/Variant.h"
|
||||
|
@ -174,6 +175,43 @@ public:
|
|||
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
|
||||
* surface one at a time by repeatedly calling a lambda that yields pixels.
|
||||
|
@ -448,6 +486,50 @@ protected:
|
|||
|
||||
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
|
||||
* WritePixelsToRow(). Those methods differ only in their behavior after a row
|
||||
|
@ -558,6 +640,20 @@ public:
|
|||
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
|
||||
* 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>
|
||||
NextPixel<PixelSize>
|
||||
nsGIFDecoder2::YieldPixel(const uint8_t* aData,
|
||||
size_t aLength,
|
||||
size_t* aBytesReadOut)
|
||||
Tuple<int32_t, Maybe<WriteState>>
|
||||
nsGIFDecoder2::YieldPixels(const uint8_t* aData,
|
||||
size_t aLength,
|
||||
size_t* aBytesReadOut,
|
||||
PixelSize* aPixelBlock,
|
||||
int32_t aBlockSize)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
MOZ_ASSERT(aBytesReadOut);
|
||||
|
@ -304,108 +306,119 @@ nsGIFDecoder2::YieldPixel(const uint8_t* aData,
|
|||
// Advance to the next byte we should read.
|
||||
const uint8_t* data = aData + *aBytesReadOut;
|
||||
|
||||
// If we don't have any decoded data to yield, try to read some input and
|
||||
// produce some.
|
||||
if (mGIFStruct.stackp == mGIFStruct.stack) {
|
||||
while (mGIFStruct.bits < mGIFStruct.codesize && *aBytesReadOut < aLength) {
|
||||
// Feed the next byte into the decoder's 32-bit input buffer.
|
||||
mGIFStruct.datum += int32_t(*data) << mGIFStruct.bits;
|
||||
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.
|
||||
int32_t written = 0;
|
||||
while (aBlockSize > written) {
|
||||
// If we don't have any decoded data to yield, try to read some input and
|
||||
// produce some.
|
||||
if (mGIFStruct.stackp == mGIFStruct.stack) {
|
||||
while (mGIFStruct.bits < mGIFStruct.codesize && *aBytesReadOut < aLength) {
|
||||
// Feed the next byte into the decoder's 32-bit input buffer.
|
||||
mGIFStruct.datum += int32_t(*data) << mGIFStruct.bits;
|
||||
mGIFStruct.bits += 8;
|
||||
data += 1;
|
||||
*aBytesReadOut += 1;
|
||||
}
|
||||
|
||||
mGIFStruct.firstchar = mGIFStruct.oldcode = code;
|
||||
|
||||
// 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);
|
||||
if (mGIFStruct.bits < mGIFStruct.codesize) {
|
||||
return MakeTuple(written, Some(WriteState::NEED_MORE_DATA));
|
||||
}
|
||||
|
||||
*mGIFStruct.stackp++ = mGIFStruct.suffix[code];
|
||||
code = mGIFStruct.prefix[code];
|
||||
// Get the leading variable-length symbol from the data stream.
|
||||
int code = mGIFStruct.datum & mGIFStruct.codemask;
|
||||
mGIFStruct.datum >>= mGIFStruct.codesize;
|
||||
mGIFStruct.bits -= mGIFStruct.codesize;
|
||||
|
||||
if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
|
||||
return AsVariant(WriteState::FAILURE); // Stack overflow; something's wrong.
|
||||
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 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];
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (MOZ_UNLIKELY(mGIFStruct.stackp <= mGIFStruct.stack)) {
|
||||
MOZ_ASSERT_UNREACHABLE("No decoded data but we didn't return early?");
|
||||
return MakeTuple(written, Some(WriteState::FAILURE));
|
||||
}
|
||||
|
||||
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)) {
|
||||
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));
|
||||
return MakeTuple(written, Maybe<WriteState>());
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
auto result = mGIFStruct.images_decoded == 0
|
||||
? mPipe.WritePixels<uint32_t>([&]{ return YieldPixel<uint32_t>(data, length, &bytesRead); })
|
||||
: mPipe.WritePixels<uint8_t>([&]{ return YieldPixel<uint8_t>(data, length, &bytesRead); });
|
||||
? mPipe.WritePixelBlocks<uint32_t>([&](uint32_t* aPixelBlock, int32_t aBlockSize) {
|
||||
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)) {
|
||||
MOZ_ASSERT_UNREACHABLE("Overread?");
|
||||
|
|
|
@ -65,8 +65,12 @@ private:
|
|||
ColormapIndexToPixel(uint8_t aIndex);
|
||||
|
||||
/// A generator function that performs LZW decompression and yields pixels.
|
||||
template <typename PixelSize> NextPixel<PixelSize>
|
||||
YieldPixel(const uint8_t* aData, size_t aLength, size_t* aBytesReadOut);
|
||||
template <typename PixelSize> Tuple<int32_t, Maybe<WriteState>>
|
||||
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
|
||||
/// there's alpha, or because the frame rect doesn't cover the entire image.
|
||||
|
|
|
@ -305,12 +305,14 @@ imgTools::DecodeImageAsync(nsIInputStream* aInStr,
|
|||
*/
|
||||
static nsresult
|
||||
EncodeImageData(DataSourceSurface* aDataSurface,
|
||||
DataSourceSurface::ScopedMap& aMap,
|
||||
const nsACString& aMimeType,
|
||||
const nsAString& aOutputOptions,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8,
|
||||
"We're assuming B8G8R8A8");
|
||||
MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
aDataSurface->GetFormat() == SurfaceFormat::B8G8R8X8,
|
||||
"We're assuming B8G8R8A8/X8");
|
||||
|
||||
// Get an image encoder for the media type
|
||||
nsAutoCString encoderCID(
|
||||
|
@ -321,29 +323,37 @@ EncodeImageData(DataSourceSurface* aDataSurface,
|
|||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
IntSize size = aDataSurface->GetSize();
|
||||
uint32_t dataLength = map.mStride * size.height;
|
||||
uint32_t dataLength = aMap.GetStride() * size.height;
|
||||
|
||||
// Encode the bitmap
|
||||
nsresult rv = encoder->InitFromData(map.mData,
|
||||
nsresult rv = encoder->InitFromData(aMap.GetData(),
|
||||
dataLength,
|
||||
size.width,
|
||||
size.height,
|
||||
map.mStride,
|
||||
aMap.GetStride(),
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
aOutputOptions);
|
||||
aDataSurface->Unmap();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
encoder.forget(aStream);
|
||||
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
|
||||
imgTools::EncodeImage(imgIContainer* aContainer,
|
||||
const nsACString& aMimeType,
|
||||
|
@ -358,7 +368,8 @@ imgTools::EncodeImage(imgIContainer* aContainer,
|
|||
|
||||
RefPtr<DataSourceSurface> dataSurface;
|
||||
|
||||
if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
|
||||
if (frame->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
frame->GetFormat() == SurfaceFormat::B8G8R8X8) {
|
||||
dataSurface = frame->GetDataSurface();
|
||||
} else {
|
||||
// Convert format to SurfaceFormat::B8G8R8A8
|
||||
|
@ -407,22 +418,33 @@ imgTools::EncodeScaledImage(imgIContainer* aContainer,
|
|||
imgIContainer::FLAG_SYNC_DECODE);
|
||||
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 =
|
||||
Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
|
||||
if (NS_WARN_IF(!dataSurface)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
|
||||
DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> dt =
|
||||
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
||||
map.mData,
|
||||
Factory::CreateDrawTargetForData(BackendType::SKIA,
|
||||
map.GetData(),
|
||||
dataSurface->GetSize(),
|
||||
map.mStride,
|
||||
map.GetStride(),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!dt) {
|
||||
gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
|
||||
|
@ -436,9 +458,7 @@ imgTools::EncodeScaledImage(imgIContainer* aContainer,
|
|||
DrawSurfaceOptions(),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
|
||||
dataSurface->Unmap();
|
||||
|
||||
return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
|
||||
return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -492,16 +512,16 @@ imgTools::EncodeCroppedImage(imgIContainer* aContainer,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
|
||||
DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> dt =
|
||||
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
||||
map.mData,
|
||||
Factory::CreateDrawTargetForData(BackendType::SKIA,
|
||||
map.GetData(),
|
||||
dataSurface->GetSize(),
|
||||
map.mStride,
|
||||
map.GetStride(),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!dt) {
|
||||
gfxWarning() <<
|
||||
|
@ -512,9 +532,7 @@ imgTools::EncodeCroppedImage(imgIContainer* aContainer,
|
|||
IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
|
||||
IntPoint(0, 0));
|
||||
|
||||
dataSurface->Unmap();
|
||||
|
||||
return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
|
||||
return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -201,6 +201,22 @@ TEST_F(ImageSurfacePipeIntegration, SurfacePipe)
|
|||
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.
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
@ -281,6 +297,22 @@ TEST_F(ImageSurfacePipeIntegration, PalettedSurfacePipe)
|
|||
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.
|
||||
{
|
||||
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)
|
||||
{
|
||||
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
|
||||
|
|
|
@ -150,7 +150,7 @@ class Builder {
|
|||
// A rooted reference to our 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.assertBuilt(value_);
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
//
|
||||
// The GCPolicy provides at a minimum:
|
||||
//
|
||||
// static T initial()
|
||||
// - Construct and return an empty T.
|
||||
//
|
||||
// static void trace(JSTracer, T* tp, const char* name)
|
||||
// - Trace the edge |*tp|, calling the edge |name|. Containers like
|
||||
// GCHashMap and GCHashSet use this method to trace their children.
|
||||
|
@ -72,10 +69,6 @@ namespace JS {
|
|||
template <typename T>
|
||||
struct StructGCPolicy
|
||||
{
|
||||
static T initial() {
|
||||
return T();
|
||||
}
|
||||
|
||||
static void trace(JSTracer* trc, T* tp, const char* name) {
|
||||
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.
|
||||
template <typename T>
|
||||
struct IgnoreGCPolicy {
|
||||
static T initial() { return T(); }
|
||||
static void trace(JSTracer* trc, T* t, const char* name) {}
|
||||
static bool needsSweep(T* v) { return false; }
|
||||
static bool isValid(const T& v) { return true; }
|
||||
|
@ -113,7 +105,6 @@ template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
|
|||
template <typename T>
|
||||
struct GCPointerPolicy
|
||||
{
|
||||
static T initial() { return nullptr; }
|
||||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||
if (*vp)
|
||||
js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
|
||||
|
@ -140,7 +131,6 @@ template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
|
|||
template <typename T>
|
||||
struct NonGCPointerPolicy
|
||||
{
|
||||
static T initial() { return nullptr; }
|
||||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||
if (*vp)
|
||||
(*vp)->trace(trc);
|
||||
|
@ -170,7 +160,6 @@ struct GCPolicy<JS::Heap<T>>
|
|||
template <typename T, typename 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) {
|
||||
if (tp->get())
|
||||
GCPolicy<T>::trace(trc, tp->get(), name);
|
||||
|
@ -192,7 +181,6 @@ struct GCPolicy<mozilla::UniquePtr<T, D>>
|
|||
template <typename 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) {
|
||||
if (tp->isSome())
|
||||
GCPolicy<T>::trace(trc, tp->ptr(), name);
|
||||
|
|
|
@ -112,9 +112,6 @@ struct GCPolicy<mozilla::Variant<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) {
|
||||
Impl::trace(trc, v, name);
|
||||
}
|
||||
|
|
|
@ -176,7 +176,6 @@ namespace JS {
|
|||
template <>
|
||||
struct GCPolicy<jsid>
|
||||
{
|
||||
static jsid initial() { return JSID_VOID; }
|
||||
static void trace(JSTracer* trc, jsid* idp, const char* 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) 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
|
||||
/**
|
||||
* 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() {
|
||||
static_assert(sizeof(T) == sizeof(Heap<T>),
|
||||
"Heap<T> must be binary compatible with T.");
|
||||
init(GCPolicy<T>::initial());
|
||||
init(SafelyInitialized<T>());
|
||||
}
|
||||
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); }
|
||||
|
||||
~Heap() {
|
||||
post(ptr, GCPolicy<T>::initial());
|
||||
post(ptr, SafelyInitialized<T>());
|
||||
}
|
||||
|
||||
DECLARE_POINTER_CONSTREF_OPS(T);
|
||||
|
@ -291,7 +325,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>>
|
|||
private:
|
||||
void init(const T& newPtr) {
|
||||
ptr = newPtr;
|
||||
post(GCPolicy<T>::initial(), ptr);
|
||||
post(SafelyInitialized<T>(), ptr);
|
||||
}
|
||||
|
||||
void set(const T& newPtr) {
|
||||
|
@ -950,7 +984,7 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
|
|||
|
||||
template <typename RootingContext>
|
||||
explicit Rooted(const RootingContext& cx)
|
||||
: ptr(GCPolicy<T>::initial())
|
||||
: ptr(SafelyInitialized<T>())
|
||||
{
|
||||
registerWithRootLists(rootLists(cx));
|
||||
}
|
||||
|
@ -1219,16 +1253,16 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
|||
public:
|
||||
using ElementType = T;
|
||||
|
||||
PersistentRooted() : ptr(GCPolicy<T>::initial()) {}
|
||||
PersistentRooted() : ptr(SafelyInitialized<T>()) {}
|
||||
|
||||
explicit PersistentRooted(RootingContext* cx)
|
||||
: ptr(GCPolicy<T>::initial())
|
||||
: ptr(SafelyInitialized<T>())
|
||||
{
|
||||
registerWithRootLists(cx);
|
||||
}
|
||||
|
||||
explicit PersistentRooted(JSContext* cx)
|
||||
: ptr(GCPolicy<T>::initial())
|
||||
: ptr(SafelyInitialized<T>())
|
||||
{
|
||||
registerWithRootLists(RootingContext::get(cx));
|
||||
}
|
||||
|
@ -1248,7 +1282,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
|||
}
|
||||
|
||||
explicit PersistentRooted(JSRuntime* rt)
|
||||
: ptr(GCPolicy<T>::initial())
|
||||
: ptr(SafelyInitialized<T>())
|
||||
{
|
||||
registerWithRootLists(rt);
|
||||
}
|
||||
|
@ -1280,7 +1314,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
|||
}
|
||||
|
||||
void init(JSContext* cx) {
|
||||
init(cx, GCPolicy<T>::initial());
|
||||
init(cx, SafelyInitialized<T>());
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
|
@ -1291,7 +1325,7 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
|
|||
|
||||
void reset() {
|
||||
if (initialized()) {
|
||||
set(GCPolicy<T>::initial());
|
||||
set(SafelyInitialized<T>());
|
||||
ListBase::remove();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1244,7 +1244,6 @@ JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const
|
|||
template <>
|
||||
struct GCPolicy<JS::Value>
|
||||
{
|
||||
static Value initial() { return UndefinedValue(); }
|
||||
static void trace(JSTracer* trc, Value* v, const char* 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
|
||||
* 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 arrayProto;
|
||||
|
|
|
@ -376,7 +376,7 @@ MapIteratorObject::createResultPair(JSContext* cx)
|
|||
return nullptr;
|
||||
|
||||
Rooted<TaggedProto> proto(cx, resultPairObj->taggedProto());
|
||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto);
|
||||
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, resultPairObj->getClass(), proto);
|
||||
if (!group)
|
||||
return nullptr;
|
||||
resultPairObj->setGroup(group);
|
||||
|
@ -1204,7 +1204,7 @@ SetIteratorObject::createResult(JSContext* cx)
|
|||
return nullptr;
|
||||
|
||||
Rooted<TaggedProto> proto(cx, resultObj->taggedProto());
|
||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultObj->getClass(), proto);
|
||||
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, resultObj->getClass(), proto);
|
||||
if (!group)
|
||||
return nullptr;
|
||||
resultObj->setGroup(group);
|
||||
|
|
|
@ -350,8 +350,8 @@ function InnerModuleInstantiation(module, stack, index)
|
|||
}
|
||||
|
||||
// Step 3
|
||||
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
|
||||
"Bad module status in ModuleDeclarationInstantiation");
|
||||
if (module.status !== MODULE_STATUS_UNINSTANTIATED)
|
||||
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
|
||||
|
||||
// Steps 4
|
||||
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
|
||||
* 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 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 */
|
||||
JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
JSObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
if (!templateObject)
|
||||
return false;
|
||||
|
||||
|
@ -1568,7 +1568,7 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
|
|||
|
||||
NativeObject* nproto = static_cast<NativeObject*>(proto);
|
||||
|
||||
Shape* shape = cx->compartment()->regExps.getOptimizableRegExpPrototypeShape();
|
||||
Shape* shape = cx->realm()->regExps.getOptimizableRegExpPrototypeShape();
|
||||
if (shape == nproto->lastProperty())
|
||||
return true;
|
||||
|
||||
|
@ -1635,7 +1635,7 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
|
|||
if (!has)
|
||||
return false;
|
||||
|
||||
cx->compartment()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
|
||||
cx->realm()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1659,7 +1659,7 @@ js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
|
|||
|
||||
RegExpObject* rx = &obj->as<RegExpObject>();
|
||||
|
||||
Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
|
||||
Shape* shape = cx->realm()->regExps.getOptimizableRegExpInstanceShape();
|
||||
if (shape == rx->lastProperty())
|
||||
return true;
|
||||
|
||||
|
@ -1672,7 +1672,7 @@ js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
|
|||
if (!RegExpObject::isInitialShape(rx))
|
||||
return false;
|
||||
|
||||
cx->compartment()->regExps.setOptimizableRegExpInstanceShape(rx->lastProperty());
|
||||
cx->realm()->regExps.setOptimizableRegExpInstanceShape(rx->lastProperty());
|
||||
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 */
|
||||
JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
JSObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
if (!templateObject)
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -1279,7 +1279,7 @@ static bool
|
|||
GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setNumber(cx->compartment()->savedStacks().count());
|
||||
args.rval().setNumber(cx->realm()->savedStacks().count());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1288,13 +1288,12 @@ ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
js::SavedStacks& savedStacks = cx->compartment()->savedStacks();
|
||||
js::SavedStacks& savedStacks = cx->realm()->savedStacks();
|
||||
if (savedStacks.initialized())
|
||||
savedStacks.clear();
|
||||
|
||||
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
|
||||
for (ActivationIterator iter(cx); !iter.done(); ++iter)
|
||||
iter->clearLiveSavedFrameCache();
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
|
|
|
@ -409,7 +409,7 @@ template <class T>
|
|||
class PreBarriered : public WriteBarrieredBase<T>
|
||||
{
|
||||
public:
|
||||
PreBarriered() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
||||
PreBarriered() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||
/*
|
||||
* Allow implicit construction for use in generic contexts, such as
|
||||
* DebuggerWeakMap::markKeys.
|
||||
|
@ -453,12 +453,12 @@ template <class T>
|
|||
class GCPtr : public WriteBarrieredBase<T>
|
||||
{
|
||||
public:
|
||||
GCPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
||||
GCPtr() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||
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) {
|
||||
this->post(JS::GCPolicy<T>::initial(), v);
|
||||
this->post(JS::SafelyInitialized<T>(), v);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
~GCPtr() {
|
||||
|
@ -471,7 +471,7 @@ class GCPtr : public WriteBarrieredBase<T>
|
|||
//
|
||||
// Note that when sweeping the wrapped pointer may already have been
|
||||
// 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);
|
||||
}
|
||||
#endif
|
||||
|
@ -479,7 +479,7 @@ class GCPtr : public WriteBarrieredBase<T>
|
|||
void init(const T& v) {
|
||||
CheckTargetIsNotGray(v);
|
||||
this->value = v;
|
||||
this->post(JS::GCPolicy<T>::initial(), v);
|
||||
this->post(JS::SafelyInitialized<T>(), v);
|
||||
}
|
||||
|
||||
DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
|
||||
|
@ -529,11 +529,11 @@ template <class T>
|
|||
class HeapPtr : public WriteBarrieredBase<T>
|
||||
{
|
||||
public:
|
||||
HeapPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
||||
HeapPtr() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||
|
||||
// Implicitly adding barriers is a reasonable default.
|
||||
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.
|
||||
*/
|
||||
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() {
|
||||
this->pre();
|
||||
this->post(this->value, JS::GCPolicy<T>::initial());
|
||||
this->post(this->value, JS::SafelyInitialized<T>());
|
||||
}
|
||||
|
||||
void init(const T& v) {
|
||||
CheckTargetIsNotGray(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);
|
||||
|
@ -611,17 +611,17 @@ class ReadBarriered : public ReadBarrieredBase<T>,
|
|||
using ReadBarrieredBase<T>::value;
|
||||
|
||||
public:
|
||||
ReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
||||
ReadBarriered() : ReadBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||
|
||||
// It is okay to add barriers implicitly.
|
||||
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
|
||||
// not escape, so no read barrier is necessary.
|
||||
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
|
||||
|
@ -629,11 +629,11 @@ class ReadBarriered : public ReadBarrieredBase<T>,
|
|||
ReadBarriered(ReadBarriered&& v)
|
||||
: ReadBarrieredBase<T>(mozilla::Move(v))
|
||||
{
|
||||
this->post(JS::GCPolicy<T>::initial(), v.value);
|
||||
this->post(JS::SafelyInitialized<T>(), v.value);
|
||||
}
|
||||
|
||||
~ReadBarriered() {
|
||||
this->post(this->value, JS::GCPolicy<T>::initial());
|
||||
this->post(this->value, JS::SafelyInitialized<T>());
|
||||
}
|
||||
|
||||
ReadBarriered& operator=(const ReadBarriered& v) {
|
||||
|
|
|
@ -2577,7 +2577,7 @@ GCRuntime::sweepZoneAfterCompacting(Zone* zone)
|
|||
jitZone->sweep();
|
||||
|
||||
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
|
||||
r->objectGroups.sweep();
|
||||
r->sweepObjectGroups();
|
||||
r->sweepRegExps();
|
||||
r->sweepSavedStacks();
|
||||
r->sweepVarNames();
|
||||
|
@ -4253,8 +4253,9 @@ GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOu
|
|||
|
||||
for (RealmsIter r(rt, WithAtoms); !r.done(); r.next()) {
|
||||
r->unmark();
|
||||
r->scheduledForDestruction = false;
|
||||
r->maybeAlive = r->shouldTraceGlobal() || !r->zone()->isGCScheduled();
|
||||
JSCompartment* comp = JS::GetCompartmentForRealm(r);
|
||||
comp->scheduledForDestruction = false;
|
||||
comp->maybeAlive = r->shouldTraceGlobal() || !r->zone()->isGCScheduled();
|
||||
if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode))
|
||||
r->zone()->setPreservingCode(true);
|
||||
}
|
||||
|
@ -5424,8 +5425,8 @@ static void
|
|||
SweepObjectGroups(GCParallelTask* task)
|
||||
{
|
||||
JSRuntime* runtime = task->runtime();
|
||||
for (SweepGroupCompartmentsIter c(runtime); !c.done(); c.next())
|
||||
c->objectGroups.sweep();
|
||||
for (SweepGroupRealmsIter r(runtime); !r.done(); r.next())
|
||||
r->sweepObjectGroups();
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -5532,8 +5533,8 @@ GCRuntime::sweepDebuggerOnMainThread(FreeOp* fop)
|
|||
// table.
|
||||
{
|
||||
gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::SWEEP_MISC);
|
||||
for (SweepGroupCompartmentsIter c(rt); !c.done(); c.next())
|
||||
c->sweepDebugEnvironments();
|
||||
for (SweepGroupRealmsIter r(rt); !r.done(); r.next())
|
||||
r->sweepDebugEnvironments();
|
||||
}
|
||||
|
||||
// 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));
|
||||
if (!compartment || !compartment->init(cx))
|
||||
ScopedJSDeletePtr<Realm> realm(cx->new_<Realm>(zone, options));
|
||||
if (!realm || !realm->init(cx))
|
||||
return nullptr;
|
||||
|
||||
// Set up the principals.
|
||||
JS_SetCompartmentPrincipals(compartment, principals);
|
||||
JS_SetCompartmentPrincipals(JS::GetCompartmentForRealm(realm), principals);
|
||||
|
||||
AutoLockGC lock(rt);
|
||||
|
||||
if (!zone->compartments().append(compartment.get())) {
|
||||
if (!zone->compartments().append(JS::GetCompartmentForRealm(realm.get()))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7982,7 +7983,7 @@ js::NewCompartment(JSContext* cx, JSPrincipals* principals,
|
|||
}
|
||||
|
||||
zoneHolder.forget();
|
||||
return compartment.forget();
|
||||
return JS::GetCompartmentForRealm(realm.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -8482,12 +8483,12 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
|
|||
}
|
||||
|
||||
for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) {
|
||||
r->objectGroups.checkTablesAfterMovingGC();
|
||||
r->checkObjectGroupTablesAfterMovingGC();
|
||||
r->dtoaCache.checkCacheAfterMovingGC();
|
||||
JS::GetCompartmentForRealm(r)->checkWrapperMapAfterMovingGC();
|
||||
r->checkScriptMapsAfterMovingGC();
|
||||
if (r->debugEnvs)
|
||||
r->debugEnvs->checkHashTablesAfterMovingGC();
|
||||
if (r->debugEnvs())
|
||||
r->debugEnvs()->checkHashTablesAfterMovingGC();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,7 +23,7 @@ template <typename T>
|
|||
class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
|
||||
{
|
||||
public:
|
||||
UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
|
||||
UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
|
||||
MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
|
||||
explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
|
||||
UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
|
||||
|
@ -42,7 +42,7 @@ class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
|
|||
|
||||
const T get() const {
|
||||
if (!InternalBarrierMethods<T>::isMarkable(this->value))
|
||||
return JS::GCPolicy<T>::initial();
|
||||
return JS::SafelyInitialized<T>();
|
||||
this->read();
|
||||
return this->value;
|
||||
}
|
||||
|
|
|
@ -121,7 +121,6 @@ namespace js {
|
|||
template <typename T>
|
||||
struct InternalGCPointerPolicy {
|
||||
using Type = typename mozilla::RemovePointer<T>::Type;
|
||||
static T initial() { return nullptr; }
|
||||
static void preBarrier(T v) {
|
||||
if (v)
|
||||
Type::writeBarrierPre(v);
|
||||
|
|
|
@ -89,7 +89,7 @@ class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
|
|||
using ElementType = T;
|
||||
|
||||
template <typename CX>
|
||||
explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
|
||||
explicit FakeRooted(CX* cx) : ptr(JS::SafelyInitialized<T>()) {}
|
||||
|
||||
template <typename CX>
|
||||
FakeRooted(CX* cx, T initial) : ptr(initial) {}
|
||||
|
|
|
@ -2,7 +2,7 @@ if (!('oomTest' in this))
|
|||
quit();
|
||||
|
||||
oomTest(() => {
|
||||
var g = newGlobal();
|
||||
var g = newGlobal({sameZoneAs: this});
|
||||
g.eval("\
|
||||
function f(){}; \
|
||||
getLcovInfo(); \
|
||||
|
|
|
@ -10,5 +10,5 @@ for (var i = 0; i < 9; ++i) {
|
|||
}
|
||||
// jsfunfuzz-generated
|
||||
oomTest(function() {
|
||||
newGlobal();
|
||||
newGlobal({sameZoneAs: this});
|
||||
})
|
||||
|
|
|
@ -8,5 +8,5 @@ dbg.onNewGlobalObject = function(global) {
|
|||
dbg.memory.takeCensus({});
|
||||
};
|
||||
oomTest(function() {
|
||||
newGlobal({})
|
||||
newGlobal({sameZoneAs: this})
|
||||
});
|
||||
|
|
|
@ -9,5 +9,5 @@ fullcompartmentchecks(true);
|
|||
var dbg = new Debugger;
|
||||
dbg.onNewGlobalObject = function() {};
|
||||
oomTest(function() {
|
||||
newGlobal();
|
||||
newGlobal({sameZoneAs: this});
|
||||
})
|
||||
|
|
|
@ -12,6 +12,6 @@ for (var i = 0; i < 9; ++ i) {
|
|||
dbg.onNewGlobalObject = ERROR;
|
||||
}
|
||||
oomTest(function() {
|
||||
newGlobal();
|
||||
newGlobal({sameZoneAs: this});
|
||||
})
|
||||
`);
|
||||
|
|
|
@ -10,6 +10,6 @@ dbg.onNewScript = function (s) {
|
|||
}
|
||||
log = "";
|
||||
oomTest(() => {
|
||||
var static = newGlobal();
|
||||
var static = newGlobal({sameZoneAs: this});
|
||||
g.eval("(function() {})()");
|
||||
});
|
||||
|
|
|
@ -18,6 +18,5 @@ dbg.onNewGlobalObject = function(global) {
|
|||
get.seen = true;
|
||||
};
|
||||
oomTest(function() {
|
||||
newGlobal({
|
||||
})
|
||||
newGlobal({sameZoneAs: this})
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ loadFile(`
|
|||
var global = this;
|
||||
var p = new Proxy(o, {
|
||||
"deleteProperty": function (await , key) {
|
||||
var g = newGlobal();
|
||||
var g = newGlobal({sameZoneAs: this});
|
||||
g.parent = global;
|
||||
g.eval("var dbg = new Debugger(parent); dbg.onEnterFrame = function(frame) {};");
|
||||
}
|
||||
|
|
|
@ -14,6 +14,6 @@ function ERROR(msg) {
|
|||
var dbg = new Debugger;
|
||||
dbg.onNewGlobalObject = ERROR;
|
||||
oomTest(function() {
|
||||
newGlobal();
|
||||
newGlobal({sameZoneAs: this});
|
||||
})
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
if (!('oomTest' in this))
|
||||
quit();
|
||||
|
||||
var source = `
|
||||
var global = newGlobal();
|
||||
function x() {
|
||||
var global = newGlobal({sameZoneAs: this});
|
||||
global.eval('function f() { debugger; }');
|
||||
var debug = new Debugger(global);
|
||||
var foo;
|
||||
|
@ -13,8 +13,6 @@ var source = `
|
|||
return null;
|
||||
};
|
||||
global.eval('f(0)');
|
||||
`;
|
||||
function test() {
|
||||
oomTest(new Function(source), false);
|
||||
}
|
||||
test();
|
||||
|
||||
oomTest(x, false);
|
||||
|
|
|
@ -4,6 +4,6 @@ if (!('oomTest' in this))
|
|||
quit();
|
||||
|
||||
oomTest(() => {
|
||||
var g = newGlobal();
|
||||
var g = newGlobal({sameZoneAs: this});
|
||||
g.eval("(function() {})()");
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ if (!('oomTest' in this))
|
|||
quit();
|
||||
|
||||
oomTest(() => {
|
||||
let global = newGlobal();
|
||||
let global = newGlobal({sameZoneAs: this});
|
||||
Debugger(global).onDebuggerStatement = function (frame) {
|
||||
frame.eval("f")
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ if (!('oomTest' in this))
|
|||
|
||||
var x = ``.split();
|
||||
oomTest(function() {
|
||||
var lfGlobal = newGlobal();
|
||||
var lfGlobal = newGlobal({sameZoneAs: this});
|
||||
for (lfLocal in this) {
|
||||
if (!(lfLocal in lfGlobal)) {
|
||||
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;
|
||||
|
||||
// These types should work:
|
||||
assertEq(new Global({type: "i32"}) instanceof Global, true);
|
||||
assertEq(new Global({type: "f32"}) instanceof Global, true);
|
||||
assertEq(new Global({type: "f64"}) instanceof Global, true);
|
||||
assertEq(new Global({value: "i32"}) instanceof Global, true);
|
||||
assertEq(new Global({value: "f32"}) instanceof Global, true);
|
||||
assertEq(new Global({value: "f64"}) instanceof Global, true);
|
||||
|
||||
// These types should not work:
|
||||
assertErrorMessage(() => new Global({type: "i64"}), 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(), TypeError, /Global requires more than 0 arguments/);
|
||||
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({value: "fnord"}), TypeError, /bad type for a WebAssembly.Global/);
|
||||
assertErrorMessage(() => new Global(), TypeError, /Global requires more than 0 arguments/);
|
||||
|
||||
// Coercion of init value; ".value" accessor
|
||||
assertEq((new Global({type: "i32"}, 3.14)).value, 3);
|
||||
assertEq((new Global({type: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
|
||||
assertEq((new Global({type: "f64"}, "3.25")).value, 3.25);
|
||||
assertEq((new Global({value: "i32"}, 3.14)).value, 3);
|
||||
assertEq((new Global({value: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
|
||||
assertEq((new Global({value: "f64"}, "3.25")).value, 3.25);
|
||||
|
||||
// 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.
|
||||
assertEq((new Global({type: "i32"})).value, 0);
|
||||
assertEq((new Global({type: "f32"})).value, 0);
|
||||
assertEq((new Global({type: "f64"})).value, 0);
|
||||
assertEq((new Global({value: "i32"})).value, 0);
|
||||
assertEq((new Global({value: "f32"})).value, 0);
|
||||
assertEq((new Global({value: "f64"})).value, 0);
|
||||
|
||||
{
|
||||
// "value" is enumerable
|
||||
let x = new Global({type: "i32"});
|
||||
let x = new Global({value: "i32"});
|
||||
let s = "";
|
||||
for ( let i in x )
|
||||
s = s + i + ",";
|
||||
|
@ -386,20 +386,20 @@ if (typeof WebAssembly.Global === "function") {
|
|||
assertEq("value" in Global.prototype, true);
|
||||
|
||||
// Can't set the value of an immutable global
|
||||
assertErrorMessage(() => (new Global({type: "i32"})).value = 10,
|
||||
assertErrorMessage(() => (new Global({value: "i32"})).value = 10,
|
||||
TypeError,
|
||||
/can't set value of immutable 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;
|
||||
assertEq(g.value, 10);
|
||||
}
|
||||
|
||||
{
|
||||
// Misc internal conversions
|
||||
let g = new Global({type: "i32"}, 42);
|
||||
let g = new Global({value: "i32"}, 42);
|
||||
|
||||
// valueOf
|
||||
assertEq(g - 5, 37);
|
||||
|
@ -513,7 +513,7 @@ if (typeof WebAssembly.Global === "function") {
|
|||
(import "m" "g" (global i32)))`));
|
||||
|
||||
// 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}}),
|
||||
LinkError,
|
||||
mutErr);
|
||||
|
@ -522,7 +522,7 @@ if (typeof WebAssembly.Global === "function") {
|
|||
(import "m" "g" (global (mut i32))))`));
|
||||
|
||||
// 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}}),
|
||||
LinkError,
|
||||
mutErr);
|
||||
|
|
|
@ -6,7 +6,7 @@ if (typeof oomTest !== 'function' || !wasmIsSupported()) {
|
|||
}
|
||||
|
||||
function foo() {
|
||||
var g = newGlobal();
|
||||
var g = newGlobal({sameZoneAs: this});
|
||||
g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
|
||||
}
|
||||
oomTest(foo);
|
||||
|
|
|
@ -83,8 +83,8 @@ BaselineFrame::trace(JSTracer* trc, const JSJitFrameIter& frameIterator)
|
|||
TraceLocals(this, trc, 0, nlivefixed);
|
||||
}
|
||||
|
||||
if (script->compartment()->debugEnvs)
|
||||
script->compartment()->debugEnvs->traceLiveFrame(trc, this);
|
||||
if (auto* debugEnvs = script->realm()->debugEnvs())
|
||||
debugEnvs->traceLiveFrame(trc, this);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -4406,7 +4406,7 @@ CallIRGenerator::tryAttachStringSplit()
|
|||
return false;
|
||||
|
||||
// Get the object group to use for this location.
|
||||
RootedObjectGroup group(cx_, ObjectGroupCompartment::getStringSplitStringGroup(cx_));
|
||||
RootedObjectGroup group(cx_, ObjectGroupRealm::getStringSplitStringGroup(cx_));
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -1930,7 +1930,7 @@ JitRealm::generateRegExpMatcherStub(JSContext* cx)
|
|||
maybeTemp4 = regs.takeAny();
|
||||
}
|
||||
|
||||
ArrayObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
ArrayObject* templateObject = cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
if (!templateObject)
|
||||
return nullptr;
|
||||
|
||||
|
@ -2543,7 +2543,7 @@ CodeGenerator::visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
|
|||
masm.loadJSContext(temp);
|
||||
masm.loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
|
||||
size_t offset = Realm::offsetOfRegExps() +
|
||||
RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape();
|
||||
RegExpRealm::offsetOfOptimizableRegExpPrototypeShape();
|
||||
masm.loadPtr(Address(temp, offset), temp);
|
||||
|
||||
masm.branchTestObjShapeUnsafe(Assembler::NotEqual, object, temp, ool->entry());
|
||||
|
@ -2603,7 +2603,7 @@ CodeGenerator::visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
|
|||
masm.loadJSContext(temp);
|
||||
masm.loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
|
||||
size_t offset = Realm::offsetOfRegExps() +
|
||||
RegExpCompartment::offsetOfOptimizableRegExpInstanceShape();
|
||||
RegExpRealm::offsetOfOptimizableRegExpInstanceShape();
|
||||
masm.loadPtr(Address(temp, offset), temp);
|
||||
|
||||
masm.branchTestObjShapeUnsafe(Assembler::NotEqual, object, temp, ool->entry());
|
||||
|
|
|
@ -1770,7 +1770,7 @@ IonBuilder::inlineStringSplitString(CallInfo& callInfo)
|
|||
return resultConstStringSplit;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
ObjectGroup* group = ObjectGroupCompartment::getStringSplitStringGroup(cx);
|
||||
ObjectGroup* group = ObjectGroupRealm::getStringSplitStringGroup(cx);
|
||||
if (!group)
|
||||
return InliningStatus_NotInlined;
|
||||
AutoSweepObjectGroup sweep(group);
|
||||
|
|
|
@ -1129,7 +1129,7 @@ RStringSplit::recover(JSContext* cx, SnapshotIterator& iter) const
|
|||
{
|
||||
RootedString str(cx, iter.read().toString());
|
||||
RootedString sep(cx, iter.read().toString());
|
||||
RootedObjectGroup group(cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
|
||||
RootedObjectGroup group(cx, ObjectGroupRealm::getStringSplitStringGroup(cx));
|
||||
if (!group) {
|
||||
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_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_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_BAD_SIG, 0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
|
||||
MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
|
||||
|
|
|
@ -682,7 +682,7 @@ JS::EnterRealm(JSContext* cx, JSObject* target)
|
|||
|
||||
Realm* oldRealm = cx->realm();
|
||||
cx->enterRealmOf(target);
|
||||
return JS::GetRealmForCompartment(oldRealm);
|
||||
return JS::GetCompartmentForRealm(oldRealm);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
@ -7796,11 +7796,11 @@ JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp,
|
|||
{
|
||||
AssertHeapIsIdle();
|
||||
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);
|
||||
if (!compartment->savedStacks().saveCurrentStack(cx, &frame, mozilla::Move(capture)))
|
||||
if (!realm->savedStacks().saveCurrentStack(cx, &frame, mozilla::Move(capture)))
|
||||
return false;
|
||||
stackp.set(frame.get());
|
||||
return true;
|
||||
|
@ -7813,13 +7813,12 @@ JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
|
|||
{
|
||||
AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
|
||||
JSCompartment* compartment = cx->compartment();
|
||||
Realm* realm = cx->realm();
|
||||
Rooted<SavedFrame*> frame(cx);
|
||||
if (!compartment->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause,
|
||||
&frame, maxFrameCount))
|
||||
if (!realm->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause, &frame, maxFrameCount))
|
||||
return false;
|
||||
stackp.set(frame.get());
|
||||
return true;
|
||||
|
|
|
@ -447,7 +447,7 @@ ErrorCopier::~ErrorCopier()
|
|||
|
||||
// The provenance of Debugger.DebuggeeWouldRun is the topmost locking
|
||||
// debugger compartment; it should not be copied around.
|
||||
if (ar->origin() != cx->compartment() &&
|
||||
if (JS::GetCompartmentForRealm(ar->origin()) != cx->compartment() &&
|
||||
cx->isExceptionPending() &&
|
||||
!cx->isThrowingDebuggeeWouldRun())
|
||||
{
|
||||
|
|
|
@ -172,6 +172,15 @@ enum JSShellExitCode {
|
|||
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
|
||||
* js/xpconnect/src/XPCJSContext.cpp
|
||||
|
@ -610,8 +619,7 @@ ShellContext::ShellContext(JSContext* cx)
|
|||
readLineBufPos(0),
|
||||
errFilePtr(nullptr),
|
||||
outFilePtr(nullptr),
|
||||
offThreadMonitor(mutexid::ShellOffThreadState),
|
||||
moduleResolveHook(cx)
|
||||
offThreadMonitor(mutexid::ShellOffThreadState)
|
||||
{}
|
||||
|
||||
ShellContext::~ShellContext()
|
||||
|
@ -4279,8 +4287,8 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
sc->moduleResolveHook = &args[0].toObject().as<JSFunction>();
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
|
@ -4289,18 +4297,20 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
|
|||
static JSObject*
|
||||
CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier)
|
||||
{
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
if (!sc->moduleResolveHook) {
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
RootedValue hookValue(cx, global->getReservedSlot(GlobalAppSlotModuleResolveHook));
|
||||
if (hookValue.isUndefined()) {
|
||||
JS_ReportErrorASCII(cx, "Module resolve hook not set");
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
|
||||
|
||||
JS::AutoValueArray<2> args(cx);
|
||||
args[0].setObject(*module);
|
||||
args[1].setString(specifier);
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!JS_CallFunction(cx, nullptr, sc->moduleResolveHook, args, &result))
|
||||
if (!JS_CallFunctionValue(cx, nullptr, hookValue, args, &result))
|
||||
return nullptr;
|
||||
|
||||
if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
|
||||
|
|
|
@ -180,8 +180,6 @@ struct ShellContext
|
|||
// Off-thread parse state.
|
||||
js::Monitor offThreadMonitor;
|
||||
Vector<OffThreadJob*, 0, SystemAllocPolicy> offThreadJobs;
|
||||
|
||||
JS::PersistentRootedFunction moduleResolveHook;
|
||||
};
|
||||
|
||||
extern ShellContext*
|
||||
|
|
|
@ -2742,9 +2742,8 @@ Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx, AbstractFramePtr fr
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::ensureExecutionObservabilityOfCompartment(JSContext* cx, JSCompartment* comp)
|
||||
Debugger::ensureExecutionObservabilityOfRealm(JSContext* cx, Realm* realm)
|
||||
{
|
||||
Realm* realm = JS::GetRealmForCompartment(comp);
|
||||
if (realm->debuggerObservesAllExecution())
|
||||
return true;
|
||||
ExecutionObservableRealms obs(cx);
|
||||
|
@ -3746,7 +3745,7 @@ Debugger::addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp)
|
|||
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
|
||||
if (r == dbg->object->realm() || r->creationOptions().invisibleToDebugger())
|
||||
continue;
|
||||
r->scheduledForDestruction = false;
|
||||
JS::GetCompartmentForRealm(r)->scheduledForDestruction = false;
|
||||
GlobalObject* global = r->maybeGlobal();
|
||||
if (global) {
|
||||
Rooted<GlobalObject*> rg(cx, global);
|
||||
|
@ -4078,7 +4077,7 @@ Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global)
|
|||
debuggeeRealm->updateDebuggerObservesAsmJS();
|
||||
debuggeeRealm->updateDebuggerObservesBinarySource();
|
||||
debuggeeRealm->updateDebuggerObservesCoverage();
|
||||
if (observesAllExecution() && !ensureExecutionObservabilityOfCompartment(cx, debuggeeRealm))
|
||||
if (observesAllExecution() && !ensureExecutionObservabilityOfRealm(cx, debuggeeRealm))
|
||||
return false;
|
||||
|
||||
globalDebuggersGuard.release();
|
||||
|
@ -4963,7 +4962,7 @@ Debugger::findAllGlobals(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (r->creationOptions().invisibleToDebugger())
|
||||
continue;
|
||||
|
||||
r->scheduledForDestruction = false;
|
||||
JS::GetCompartmentForRealm(r)->scheduledForDestruction = false;
|
||||
|
||||
GlobalObject* global = r->maybeGlobal();
|
||||
|
||||
|
|
|
@ -754,8 +754,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
private:
|
||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(JSContext* cx,
|
||||
AbstractFramePtr frame);
|
||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfCompartment(JSContext* cx,
|
||||
JSCompartment* comp);
|
||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfRealm(JSContext* cx,
|
||||
JS::Realm* realm);
|
||||
|
||||
static bool hookObservesAllExecution(Hook which);
|
||||
|
||||
|
|
|
@ -308,10 +308,10 @@ DebuggerMemory::setAllocationSamplingProbability(JSContext* cx, unsigned argc, V
|
|||
dbg->allocationSamplingProbability = probability;
|
||||
|
||||
// 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) {
|
||||
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_));
|
||||
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)
|
||||
return nullptr;
|
||||
|
||||
|
@ -2314,7 +2316,7 @@ const DebugEnvironmentProxyHandler DebugEnvironmentProxyHandler::singleton;
|
|||
/* static */ DebugEnvironmentProxy*
|
||||
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>());
|
||||
|
||||
RootedValue priv(cx, ObjectValue(env));
|
||||
|
@ -2514,11 +2516,11 @@ CanUseDebugEnvironmentMaps(JSContext* cx)
|
|||
}
|
||||
|
||||
DebugEnvironments*
|
||||
DebugEnvironments::ensureCompartmentData(JSContext* cx)
|
||||
DebugEnvironments::ensureRealmData(JSContext* cx)
|
||||
{
|
||||
JSCompartment* c = cx->compartment();
|
||||
if (c->debugEnvs)
|
||||
return c->debugEnvs.get();
|
||||
Realm* realm = cx->realm();
|
||||
if (auto* debugEnvs = realm->debugEnvs())
|
||||
return debugEnvs;
|
||||
|
||||
auto debugEnvs = cx->make_unique<DebugEnvironments>(cx, cx->zone());
|
||||
if (!debugEnvs || !debugEnvs->init()) {
|
||||
|
@ -2526,14 +2528,14 @@ DebugEnvironments::ensureCompartmentData(JSContext* cx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
c->debugEnvs = Move(debugEnvs);
|
||||
return c->debugEnvs.get();
|
||||
realm->debugEnvsRef() = Move(debugEnvs);
|
||||
return realm->debugEnvs();
|
||||
}
|
||||
|
||||
/* static */ DebugEnvironmentProxy*
|
||||
DebugEnvironments::hasDebugEnvironment(JSContext* cx, EnvironmentObject& env)
|
||||
{
|
||||
DebugEnvironments* envs = env.compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = env.realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return nullptr;
|
||||
|
||||
|
@ -2549,13 +2551,13 @@ DebugEnvironments::hasDebugEnvironment(JSContext* cx, EnvironmentObject& env)
|
|||
DebugEnvironments::addDebugEnvironment(JSContext* cx, Handle<EnvironmentObject*> env,
|
||||
Handle<DebugEnvironmentProxy*> debugEnv)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment() == env->compartment());
|
||||
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
|
||||
MOZ_ASSERT(cx->realm() == env->realm());
|
||||
MOZ_ASSERT(cx->realm() == debugEnv->realm());
|
||||
|
||||
if (!CanUseDebugEnvironmentMaps(cx))
|
||||
return true;
|
||||
|
||||
DebugEnvironments* envs = ensureCompartmentData(cx);
|
||||
DebugEnvironments* envs = ensureRealmData(cx);
|
||||
if (!envs)
|
||||
return false;
|
||||
|
||||
|
@ -2567,7 +2569,7 @@ DebugEnvironments::hasDebugEnvironment(JSContext* cx, const EnvironmentIter& ei)
|
|||
{
|
||||
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
||||
|
||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return nullptr;
|
||||
|
||||
|
@ -2583,7 +2585,7 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
|
|||
Handle<DebugEnvironmentProxy*> debugEnv)
|
||||
{
|
||||
MOZ_ASSERT(!ei.hasSyntacticEnvironment());
|
||||
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
|
||||
MOZ_ASSERT(cx->realm() == debugEnv->realm());
|
||||
// Generators should always have environments.
|
||||
MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
|
||||
!ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator() &&
|
||||
|
@ -2592,7 +2594,7 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
|
|||
if (!CanUseDebugEnvironmentMaps(cx))
|
||||
return true;
|
||||
|
||||
DebugEnvironments* envs = ensureCompartmentData(cx);
|
||||
DebugEnvironments* envs = ensureRealmData(cx);
|
||||
if (!envs)
|
||||
return false;
|
||||
|
||||
|
@ -2717,7 +2719,7 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame)
|
|||
{
|
||||
assertSameCompartment(cx, frame);
|
||||
|
||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return;
|
||||
|
||||
|
@ -2759,7 +2761,7 @@ DebugEnvironments::onPopLexical(JSContext* cx, AbstractFramePtr frame, jsbytecod
|
|||
{
|
||||
assertSameCompartment(cx, frame);
|
||||
|
||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return;
|
||||
|
||||
|
@ -2771,7 +2773,7 @@ template <typename Environment, typename Scope>
|
|||
void
|
||||
DebugEnvironments::onPopGeneric(JSContext* cx, const EnvironmentIter& ei)
|
||||
{
|
||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return;
|
||||
|
||||
|
@ -2807,7 +2809,7 @@ DebugEnvironments::onPopVar(JSContext* cx, AbstractFramePtr frame, jsbytecode* p
|
|||
{
|
||||
assertSameCompartment(cx, frame);
|
||||
|
||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return;
|
||||
|
||||
|
@ -2827,14 +2829,15 @@ DebugEnvironments::onPopVar(JSContext* cx, const EnvironmentIter& ei)
|
|||
void
|
||||
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>());
|
||||
}
|
||||
|
||||
void
|
||||
DebugEnvironments::onCompartmentUnsetIsDebuggee(JSCompartment* c)
|
||||
DebugEnvironments::onRealmUnsetIsDebuggee(Realm* realm)
|
||||
{
|
||||
if (DebugEnvironments* envs = c->debugEnvs.get()) {
|
||||
if (DebugEnvironments* envs = realm->debugEnvs()) {
|
||||
envs->proxiedEnvs.clear();
|
||||
envs->missingEnvs.clear();
|
||||
envs->liveEnvs.clear();
|
||||
|
@ -2864,7 +2867,7 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
|||
continue;
|
||||
|
||||
AbstractFramePtr frame = i.abstractFramePtr();
|
||||
if (frame.environmentChain()->compartment() != cx->compartment())
|
||||
if (frame.environmentChain()->realm() != cx->realm())
|
||||
continue;
|
||||
|
||||
if (frame.isFunctionFrame()) {
|
||||
|
@ -2882,8 +2885,8 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
|||
|
||||
for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame(); ei++) {
|
||||
if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) {
|
||||
MOZ_ASSERT(ei.environment().compartment() == cx->compartment());
|
||||
DebugEnvironments* envs = ensureCompartmentData(cx);
|
||||
MOZ_ASSERT(ei.environment().realm() == cx->realm());
|
||||
DebugEnvironments* envs = ensureRealmData(cx);
|
||||
if (!envs)
|
||||
return false;
|
||||
if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
|
||||
|
@ -2903,7 +2906,7 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
|||
LiveEnvironmentVal*
|
||||
DebugEnvironments::hasLiveEnvironment(EnvironmentObject& env)
|
||||
{
|
||||
DebugEnvironments* envs = env.compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = env.realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return nullptr;
|
||||
|
||||
|
@ -2932,7 +2935,7 @@ DebugEnvironments::unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr until)
|
|||
if (frame == until)
|
||||
return;
|
||||
|
||||
if (frame.environmentChain()->compartment() != cx->compartment())
|
||||
if (frame.environmentChain()->realm() != cx->realm())
|
||||
continue;
|
||||
|
||||
frame.unsetPrevUpToDate();
|
||||
|
@ -2942,7 +2945,7 @@ DebugEnvironments::unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr until)
|
|||
/* static */ void
|
||||
DebugEnvironments::forwardLiveFrame(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to)
|
||||
{
|
||||
DebugEnvironments* envs = cx->compartment()->debugEnvs.get();
|
||||
DebugEnvironments* envs = cx->realm()->debugEnvs();
|
||||
if (!envs)
|
||||
return;
|
||||
|
||||
|
|
|
@ -952,7 +952,7 @@ class DebugEnvironmentProxy : public ProxyObject
|
|||
bool isOptimizedOut() const;
|
||||
};
|
||||
|
||||
/* Maintains per-compartment debug environment bookkeeping information. */
|
||||
/* Maintains per-realm debug environment bookkeeping information. */
|
||||
class DebugEnvironments
|
||||
{
|
||||
Zone* zone_;
|
||||
|
@ -993,7 +993,7 @@ class DebugEnvironments
|
|||
private:
|
||||
bool init();
|
||||
|
||||
static DebugEnvironments* ensureCompartmentData(JSContext* cx);
|
||||
static DebugEnvironments* ensureRealmData(JSContext* cx);
|
||||
|
||||
template <typename Environment, typename Scope>
|
||||
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, AbstractFramePtr frame, jsbytecode* pc);
|
||||
static void onPopWith(AbstractFramePtr frame);
|
||||
static void onCompartmentUnsetIsDebuggee(JSCompartment* c);
|
||||
static void onRealmUnsetIsDebuggee(Realm* realm);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -346,10 +346,10 @@ GlobalObject::resolveOffThreadConstructor(JSContext* cx,
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((key == JSProto_Object || key == JSProto_Function || key == JSProto_Array) &&
|
||||
!JSObject::setNewGroupUnknown(cx, placeholder->getClass(), placeholder))
|
||||
{
|
||||
return false;
|
||||
if (key == JSProto_Object || key == JSProto_Function || key == JSProto_Array) {
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||
if (!JSObject::setNewGroupUnknown(cx, realm, placeholder->getClass(), placeholder))
|
||||
return false;
|
||||
}
|
||||
|
||||
global->setPrototype(key, ObjectValue(*placeholder));
|
||||
|
|
|
@ -1660,11 +1660,11 @@ class ReservedRooted : public RootedBase<T, ReservedRooted<T>>
|
|||
}
|
||||
|
||||
explicit ReservedRooted(Rooted<T>* root) : savedRoot(root) {
|
||||
*root = JS::GCPolicy<T>::initial();
|
||||
*root = JS::SafelyInitialized<T>();
|
||||
}
|
||||
|
||||
~ReservedRooted() {
|
||||
*savedRoot = JS::GCPolicy<T>::initial();
|
||||
*savedRoot = JS::SafelyInitialized<T>();
|
||||
}
|
||||
|
||||
void set(const T& p) const { *savedRoot = p; }
|
||||
|
|
|
@ -971,8 +971,8 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
|
|||
|
||||
// Create a new group for the template.
|
||||
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
||||
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(),
|
||||
proto));
|
||||
RootedObjectGroup group(cx, ObjectGroupRealm::makeGroup(cx, templateObject->getClass(),
|
||||
proto));
|
||||
if (!group)
|
||||
return iterResultTemplate_; // = nullptr
|
||||
templateObject->setGroup(group);
|
||||
|
|
|
@ -43,10 +43,7 @@ using mozilla::PodArrayZero;
|
|||
|
||||
JSCompartment::JSCompartment(Zone* zone)
|
||||
: zone_(zone),
|
||||
runtime_(zone->runtimeFromAnyThread()),
|
||||
data(nullptr),
|
||||
regExps(),
|
||||
gcIncomingGrayPointers(nullptr)
|
||||
runtime_(zone->runtimeFromAnyThread())
|
||||
{
|
||||
runtime_->numCompartments++;
|
||||
}
|
||||
|
@ -80,17 +77,17 @@ Realm::~Realm()
|
|||
JSRuntime* rt = runtimeFromMainThread();
|
||||
if (rt->lcovOutput().isEnabled())
|
||||
rt->lcovOutput().writeLCovResult(lcovOutput);
|
||||
}
|
||||
|
||||
JSCompartment::~JSCompartment()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Avoid assertion destroying the unboxed layouts list if the embedding
|
||||
// leaked GC things.
|
||||
if (!runtime_->gc.shutdownCollectedEverything())
|
||||
unboxedLayouts.clear();
|
||||
objectGroups_.unboxedLayouts.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
JSCompartment::~JSCompartment()
|
||||
{
|
||||
runtime_->numCompartments--;
|
||||
}
|
||||
|
||||
|
@ -710,8 +707,8 @@ Realm::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMa
|
|||
return;
|
||||
|
||||
/* Mark debug scopes, if present */
|
||||
if (debugEnvs)
|
||||
debugEnvs->trace(trc);
|
||||
if (debugEnvs_)
|
||||
debugEnvs_->trace(trc);
|
||||
|
||||
objects_.trace(trc);
|
||||
|
||||
|
@ -757,8 +754,8 @@ ObjectRealm::finishRoots()
|
|||
void
|
||||
Realm::finishRoots()
|
||||
{
|
||||
if (debugEnvs)
|
||||
debugEnvs->finish();
|
||||
if (debugEnvs_)
|
||||
debugEnvs_->finish();
|
||||
|
||||
objects_.finishRoots();
|
||||
|
||||
|
@ -792,7 +789,7 @@ JSCompartment::sweepAfterMinorGC(JSTracer* trc)
|
|||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepSavedStacks()
|
||||
Realm::sweepSavedStacks()
|
||||
{
|
||||
savedStacks_.sweep();
|
||||
}
|
||||
|
@ -822,7 +819,7 @@ Realm::sweepJitRealm()
|
|||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepRegExps()
|
||||
Realm::sweepRegExps()
|
||||
{
|
||||
/*
|
||||
* JIT code increments activeWarmUpCounter for any RegExpShared used by jit
|
||||
|
@ -833,10 +830,10 @@ JSCompartment::sweepRegExps()
|
|||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepDebugEnvironments()
|
||||
Realm::sweepDebugEnvironments()
|
||||
{
|
||||
if (debugEnvs)
|
||||
debugEnvs->sweep();
|
||||
if (debugEnvs_)
|
||||
debugEnvs_->sweep();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -932,17 +929,22 @@ JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Realm::fixupAfterMovingGC()
|
||||
{
|
||||
purge();
|
||||
fixupGlobal();
|
||||
objectGroups_.fixupTablesAfterMovingGC();
|
||||
fixupScriptMapsAfterMovingGC();
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::fixupAfterMovingGC()
|
||||
{
|
||||
MOZ_ASSERT(zone()->isGCCompacting());
|
||||
|
||||
Realm* realm = JS::GetRealmForCompartment(this);
|
||||
|
||||
realm->purge();
|
||||
realm->fixupGlobal();
|
||||
objectGroups.fixupTablesAfterMovingGC();
|
||||
realm->fixupScriptMapsAfterMovingGC();
|
||||
realm->fixupAfterMovingGC();
|
||||
|
||||
// Sweep the wrapper map to update values (wrapper objects) in this
|
||||
// compartment that may have been moved.
|
||||
|
@ -1035,7 +1037,7 @@ Realm::purge()
|
|||
{
|
||||
dtoaCache.purge();
|
||||
newProxyCache.purge();
|
||||
objectGroups.purge();
|
||||
objectGroups_.purge();
|
||||
objects_.iteratorCache.clearAndShrink();
|
||||
arraySpeciesLookup.purge();
|
||||
}
|
||||
|
@ -1049,10 +1051,10 @@ Realm::clearTables()
|
|||
// a realm that has been used off thread into another realm and zone.
|
||||
JS::GetCompartmentForRealm(this)->assertNoCrossCompartmentWrappers();
|
||||
MOZ_ASSERT(!jitRealm_);
|
||||
MOZ_ASSERT(!debugEnvs);
|
||||
MOZ_ASSERT(!debugEnvs_);
|
||||
MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
|
||||
|
||||
objectGroups.clearTables();
|
||||
objectGroups_.clearTables();
|
||||
if (savedStacks_.initialized())
|
||||
savedStacks_.clear();
|
||||
if (varNames_.initialized())
|
||||
|
@ -1086,7 +1088,7 @@ void
|
|||
Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
MOZ_ASSERT(obj->realm() == this);
|
||||
assertSameCompartment(cx, this, obj);
|
||||
assertSameCompartment(cx, JS::GetCompartmentForRealm(this), obj);
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
|
||||
|
@ -1239,7 +1241,7 @@ Realm::unsetIsDebuggee()
|
|||
{
|
||||
if (isDebuggee()) {
|
||||
debugModeBits_ &= ~DebuggerObservesMask;
|
||||
DebugEnvironments::onCompartmentUnsetIsDebuggee(this);
|
||||
DebugEnvironments::onRealmUnsetIsDebuggee(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1371,9 +1373,9 @@ Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
|||
JSCompartment::addSizeOfExcludingThis(mallocSizeOf, crossCompartmentWrappersArg);
|
||||
|
||||
*realmObject += mallocSizeOf(this);
|
||||
objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
|
||||
tiArrayTypeTables, tiObjectTypeTables,
|
||||
realmTables);
|
||||
objectGroups_.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
|
||||
tiArrayTypeTables, tiObjectTypeTables,
|
||||
realmTables);
|
||||
wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
|
||||
|
||||
objects_.addSizeOfExcludingThis(mallocSizeOf,
|
||||
|
|
|
@ -557,10 +557,27 @@ struct JSCompartment
|
|||
JSRuntime* runtime_;
|
||||
|
||||
private:
|
||||
friend struct JSRuntime;
|
||||
friend struct JSContext;
|
||||
js::WrapperMap crossCompartmentWrappers;
|
||||
|
||||
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_; }
|
||||
const JS::Zone* zone() const { return zone_; }
|
||||
|
||||
|
@ -575,53 +592,19 @@ struct JSCompartment
|
|||
return runtime_;
|
||||
}
|
||||
|
||||
public:
|
||||
void* data;
|
||||
|
||||
protected:
|
||||
js::SavedStacks savedStacks_;
|
||||
|
||||
private:
|
||||
js::WrapperMap crossCompartmentWrappers;
|
||||
|
||||
public:
|
||||
void assertNoCrossCompartmentWrappers() {
|
||||
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:
|
||||
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t* crossCompartmentWrappersArg);
|
||||
|
||||
public:
|
||||
// Object group tables and other state in the compartment.
|
||||
js::ObjectGroupCompartment objectGroups;
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
void checkWrapperMapAfterMovingGC();
|
||||
#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:
|
||||
bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
|
||||
bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
|
||||
|
@ -689,30 +672,11 @@ struct JSCompartment
|
|||
void sweepAfterMinorGC(JSTracer* trc);
|
||||
|
||||
void sweepCrossCompartmentWrappers();
|
||||
void sweepSavedStacks();
|
||||
void sweepRegExps();
|
||||
void sweepDebugEnvironments();
|
||||
|
||||
static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
|
||||
void fixupAfterMovingGC();
|
||||
|
||||
js::SavedStacks& savedStacks() { return savedStacks_; }
|
||||
|
||||
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 {
|
||||
|
@ -782,7 +746,7 @@ class ObjectRealm
|
|||
|
||||
} // namespace js
|
||||
|
||||
class JS::Realm : public JSCompartment
|
||||
class JS::Realm : private JSCompartment
|
||||
{
|
||||
const JS::RealmCreationOptions creationOptions_;
|
||||
JS::RealmBehaviors behaviors_;
|
||||
|
@ -794,6 +758,12 @@ class JS::Realm : public JSCompartment
|
|||
js::ObjectRealm objects_;
|
||||
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
|
||||
// names declared using FunctionDeclaration, GeneratorDeclaration, and
|
||||
// VariableDeclaration declarations in global code in this realm.
|
||||
|
@ -817,6 +787,11 @@ class JS::Realm : public JSCompartment
|
|||
|
||||
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.
|
||||
JS::RealmStats* realmStats_ = nullptr;
|
||||
|
||||
|
@ -863,6 +838,8 @@ class JS::Realm : public JSCompartment
|
|||
// is enabled.
|
||||
js::coverage::LCovRealm lcovOutput;
|
||||
|
||||
js::RegExpRealm regExps;
|
||||
|
||||
js::DtoaCache dtoaCache;
|
||||
js::NewProxyCache newProxyCache;
|
||||
js::ArraySpeciesLookup arraySpeciesLookup;
|
||||
|
@ -927,6 +904,24 @@ class JS::Realm : public JSCompartment
|
|||
size_t* privateData,
|
||||
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_; }
|
||||
JS::RealmBehaviors& behaviors() { return behaviors_; }
|
||||
const JS::RealmBehaviors& behaviors() const { return behaviors_; }
|
||||
|
@ -987,18 +982,28 @@ class JS::Realm : public JSCompartment
|
|||
void finishRoots();
|
||||
|
||||
void sweepAfterMinorGC();
|
||||
void sweepDebugEnvironments();
|
||||
void sweepObjectRealm();
|
||||
void sweepRegExps();
|
||||
void sweepSelfHostingScriptSource();
|
||||
void sweepTemplateObjects();
|
||||
|
||||
void sweepObjectGroups() {
|
||||
objectGroups_.sweep();
|
||||
}
|
||||
|
||||
void clearScriptCounts();
|
||||
void clearScriptNames();
|
||||
|
||||
void purge();
|
||||
|
||||
void fixupAfterMovingGC();
|
||||
void fixupScriptMapsAfterMovingGC();
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
void checkObjectGroupTablesAfterMovingGC() {
|
||||
objectGroups_.checkTablesAfterMovingGC();
|
||||
}
|
||||
void checkScriptMapsAfterMovingGC();
|
||||
#endif
|
||||
|
||||
|
@ -1266,6 +1271,31 @@ class JS::Realm : public JSCompartment
|
|||
js::jit::JitRealm* jitRealm() {
|
||||
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 {
|
||||
|
|
|
@ -166,7 +166,6 @@ class CompartmentChecker
|
|||
|
||||
void check(InterpreterFrame* fp);
|
||||
void check(AbstractFramePtr frame);
|
||||
void check(SavedStacks* stacks);
|
||||
|
||||
void check(Handle<PropertyDescriptor> desc) {
|
||||
check(desc.object());
|
||||
|
|
|
@ -890,7 +890,8 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
|
|||
* inference to have unknown properties, to simplify handling of e.g.
|
||||
* NewFunctionClone.
|
||||
*/
|
||||
if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto))
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||
if (!JSObject::setNewGroupUnknown(cx, realm, &JSFunction::class_, functionProto))
|
||||
return nullptr;
|
||||
|
||||
return functionProto;
|
||||
|
|
|
@ -158,7 +158,8 @@ JSObject::setSingleton(JSContext* cx, js::HandleObject 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());
|
||||
if (!group)
|
||||
return false;
|
||||
|
|
|
@ -2108,7 +2108,7 @@ SetClassAndProto(JSContext* cx, HandleObject obj,
|
|||
// group so we can keep track of the interpreted function for Ion
|
||||
// inlining.
|
||||
MOZ_ASSERT(obj->is<JSFunction>());
|
||||
newGroup = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, proto);
|
||||
newGroup = ObjectGroupRealm::makeGroup(cx, &JSFunction::class_, proto);
|
||||
if (!newGroup)
|
||||
return false;
|
||||
newGroup->setInterpretedFunction(oldGroup->maybeInterpretedFunction());
|
||||
|
@ -2144,7 +2144,8 @@ JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
|
|||
|
||||
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());
|
||||
if (!group)
|
||||
return false;
|
||||
|
|
|
@ -390,7 +390,8 @@ class JSObject : public js::gc::Cell
|
|||
* properties.
|
||||
*/
|
||||
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. */
|
||||
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;
|
||||
|
||||
Rooted<TaggedProto> proto(cx, obj->taggedProto());
|
||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto,
|
||||
initialFlags);
|
||||
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, obj->getClass(), proto,
|
||||
initialFlags);
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
|
@ -340,26 +340,27 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
* allows multiple groups to be created with the same prototype. The
|
||||
* 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
|
||||
* of lazy groups in the compartment, which use a null associated object
|
||||
* (though there are only a few of these per compartment).
|
||||
* of lazy groups in the realm, which use a null associated object
|
||||
* (though there are only a few of these per realm).
|
||||
*/
|
||||
struct ObjectGroupCompartment::NewEntry
|
||||
struct ObjectGroupRealm::NewEntry
|
||||
{
|
||||
ReadBarrieredObjectGroup group;
|
||||
|
||||
|
@ -405,7 +406,7 @@ struct ObjectGroupCompartment::NewEntry
|
|||
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)
|
||||
return false;
|
||||
|
||||
|
@ -430,18 +431,18 @@ struct ObjectGroupCompartment::NewEntry
|
|||
|
||||
namespace js {
|
||||
template <>
|
||||
struct FallibleHashMethods<ObjectGroupCompartment::NewEntry>
|
||||
struct FallibleHashMethods<ObjectGroupRealm::NewEntry>
|
||||
{
|
||||
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) {
|
||||
return ObjectGroupCompartment::NewEntry::ensureHash(mozilla::Forward<Lookup>(l));
|
||||
return ObjectGroupRealm::NewEntry::ensureHash(mozilla::Forward<Lookup>(l));
|
||||
}
|
||||
};
|
||||
} // namespace js
|
||||
|
||||
class ObjectGroupCompartment::NewTable : public JS::WeakCache<js::GCHashSet<NewEntry, NewEntry,
|
||||
class ObjectGroupRealm::NewTable : public JS::WeakCache<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) {}
|
||||
};
|
||||
|
||||
/* static*/ ObjectGroupRealm&
|
||||
ObjectGroupRealm::get(ObjectGroup* group)
|
||||
{
|
||||
return group->realm()->objectGroups_;
|
||||
}
|
||||
|
||||
/* static*/ ObjectGroupRealm&
|
||||
ObjectGroupRealm::getForNewObject(JSContext* cx)
|
||||
{
|
||||
return cx->realm()->objectGroups_;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE ObjectGroup*
|
||||
ObjectGroupCompartment::DefaultNewGroupCache::lookup(const Class* clasp, TaggedProto proto,
|
||||
ObjectGroupRealm::DefaultNewGroupCache::lookup(const Class* clasp, TaggedProto proto,
|
||||
JSObject* associated)
|
||||
{
|
||||
if (group_ &&
|
||||
|
@ -503,17 +516,17 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
|||
clasp = &PlainObject::class_;
|
||||
}
|
||||
|
||||
ObjectGroupCompartment& groups = cx->compartment()->objectGroups;
|
||||
ObjectGroupRealm& groups = ObjectGroupRealm::getForNewObject(cx);
|
||||
|
||||
if (ObjectGroup* group = groups.defaultNewGroupCache.lookup(clasp, proto, associated))
|
||||
return group;
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
ObjectGroupCompartment::NewTable*& table = groups.defaultNewTable;
|
||||
ObjectGroupRealm::NewTable*& table = groups.defaultNewTable;
|
||||
|
||||
if (!table) {
|
||||
table = cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());
|
||||
table = cx->new_<ObjectGroupRealm::NewTable>(cx->zone());
|
||||
if (!table || !table->init()) {
|
||||
js_delete(table);
|
||||
table = nullptr;
|
||||
|
@ -545,8 +558,8 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
|||
}
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::NewTable::AddPtr p =
|
||||
table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated));
|
||||
ObjectGroupRealm::NewTable::AddPtr p =
|
||||
table->lookupForAdd(ObjectGroupRealm::NewEntry::Lookup(clasp, proto, associated));
|
||||
if (p) {
|
||||
ObjectGroup* group = p->group;
|
||||
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
|
||||
|
@ -562,12 +575,12 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
|||
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
|
||||
|
||||
Rooted<TaggedProto> protoRoot(cx, proto);
|
||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
|
||||
protoRoot, initialFlags);
|
||||
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
|
||||
protoRoot, initialFlags);
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated))) {
|
||||
if (!table->add(p, ObjectGroupRealm::NewEntry(group, associated))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -605,14 +618,15 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
|
|||
}
|
||||
|
||||
/* 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());
|
||||
|
||||
ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.lazyTable;
|
||||
ObjectGroupRealm::NewTable*& table = realm.lazyTable;
|
||||
|
||||
if (!table) {
|
||||
table = cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());
|
||||
table = cx->new_<ObjectGroupRealm::NewTable>(cx->zone());
|
||||
if (!table || !table->init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
js_delete(table);
|
||||
|
@ -621,8 +635,8 @@ ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto p
|
|||
}
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::NewTable::AddPtr p =
|
||||
table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr));
|
||||
ObjectGroupRealm::NewTable::AddPtr p =
|
||||
table->lookupForAdd(ObjectGroupRealm::NewEntry::Lookup(clasp, proto, nullptr));
|
||||
if (p) {
|
||||
ObjectGroup* group = p->group;
|
||||
MOZ_ASSERT(group->lazy());
|
||||
|
@ -634,12 +648,12 @@ ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto p
|
|||
|
||||
Rooted<TaggedProto> protoRoot(cx, proto);
|
||||
ObjectGroup* group =
|
||||
ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot,
|
||||
OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
|
||||
ObjectGroupRealm::makeGroup(cx, clasp, protoRoot,
|
||||
OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr))) {
|
||||
if (!table->add(p, ObjectGroupRealm::NewEntry(group, nullptr))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -648,13 +662,14 @@ ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto p
|
|||
}
|
||||
|
||||
/* 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.
|
||||
ObjectGroupCompartment::NewTable* table = cx->compartment()->objectGroups.defaultNewTable;
|
||||
ObjectGroupRealm::NewTable* table = realm.defaultNewTable;
|
||||
if (table) {
|
||||
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);
|
||||
if (p)
|
||||
MarkObjectGroupUnknownProperties(cx, p->group);
|
||||
|
@ -665,10 +680,10 @@ ObjectGroup::setDefaultNewGroupUnknown(JSContext* cx, const Class* clasp, Handle
|
|||
/* static */ bool
|
||||
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) {
|
||||
auto lookup = ObjectGroupCompartment::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr);
|
||||
auto lookup = ObjectGroupRealm::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr);
|
||||
auto p = table->lookup(lookup);
|
||||
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;
|
||||
|
||||
|
@ -833,11 +848,11 @@ ObjectGroup::newArrayObject(JSContext* cx,
|
|||
}
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::ArrayObjectTable*& table =
|
||||
cx->compartment()->objectGroups.arrayObjectTable;
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||
ObjectGroupRealm::ArrayObjectTable*& table = realm.arrayObjectTable;
|
||||
|
||||
if (!table) {
|
||||
table = cx->new_<ObjectGroupCompartment::ArrayObjectTable>();
|
||||
table = cx->new_<ObjectGroupRealm::ArrayObjectTable>();
|
||||
if (!table || !table->init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
js_delete(table);
|
||||
|
@ -846,8 +861,8 @@ ObjectGroup::newArrayObject(JSContext* cx,
|
|||
}
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::ArrayObjectKey key(elementType);
|
||||
DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable> p(cx, *table, key);
|
||||
ObjectGroupRealm::ArrayObjectKey key(elementType);
|
||||
DependentAddPtr<ObjectGroupRealm::ArrayObjectTable> p(cx, *table, key);
|
||||
|
||||
RootedObjectGroup group(cx);
|
||||
if (p) {
|
||||
|
@ -857,13 +872,13 @@ ObjectGroup::newArrayObject(JSContext* cx,
|
|||
if (!proto)
|
||||
return nullptr;
|
||||
Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
|
||||
group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto);
|
||||
group = ObjectGroupRealm::makeGroup(cx, &ArrayObject::class_, taggedProto);
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1041,10 +1056,10 @@ js::CombinePlainObjectPropertyTypes(JSContext* cx, JSObject* newObj,
|
|||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// ObjectGroupCompartment PlainObjectTable
|
||||
// ObjectGroupRealm PlainObjectTable
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ObjectGroupCompartment::PlainObjectKey
|
||||
struct ObjectGroupRealm::PlainObjectKey
|
||||
{
|
||||
jsid* properties;
|
||||
uint32_t nproperties;
|
||||
|
@ -1082,7 +1097,7 @@ struct ObjectGroupCompartment::PlainObjectKey
|
|||
}
|
||||
};
|
||||
|
||||
struct ObjectGroupCompartment::PlainObjectEntry
|
||||
struct ObjectGroupRealm::PlainObjectEntry
|
||||
{
|
||||
ReadBarrieredObjectGroup group;
|
||||
ReadBarrieredShape shape;
|
||||
|
@ -1156,11 +1171,11 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
|||
if (newKind == SingletonObject || nproperties == 0 || nproperties >= PropertyTree::MAX_HEIGHT)
|
||||
return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
|
||||
|
||||
ObjectGroupCompartment::PlainObjectTable*& table =
|
||||
cx->compartment()->objectGroups.plainObjectTable;
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||
ObjectGroupRealm::PlainObjectTable*& table = realm.plainObjectTable;
|
||||
|
||||
if (!table) {
|
||||
table = cx->new_<ObjectGroupCompartment::PlainObjectTable>();
|
||||
table = cx->new_<ObjectGroupRealm::PlainObjectTable>();
|
||||
if (!table || !table->init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
js_delete(table);
|
||||
|
@ -1169,8 +1184,8 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
|||
}
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties);
|
||||
ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookup(lookup);
|
||||
ObjectGroupRealm::PlainObjectKey::Lookup lookup(properties, nproperties);
|
||||
ObjectGroupRealm::PlainObjectTable::Ptr p = table->lookup(lookup);
|
||||
|
||||
if (!p) {
|
||||
if (!CanShareObjectGroup(properties, nproperties))
|
||||
|
@ -1181,8 +1196,8 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
|||
return nullptr;
|
||||
|
||||
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
||||
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_,
|
||||
tagged));
|
||||
RootedObjectGroup group(cx, ObjectGroupRealm::makeGroup(cx, &PlainObject::class_,
|
||||
tagged));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1234,17 +1249,17 @@ ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nprop
|
|||
AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::PlainObjectKey key;
|
||||
ObjectGroupRealm::PlainObjectKey key;
|
||||
key.properties = ids;
|
||||
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.shape.set(obj->lastProperty());
|
||||
entry.types = types;
|
||||
|
||||
ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
|
||||
ObjectGroupRealm::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
|
||||
if (!table->add(np, key, entry)) {
|
||||
ReportOutOfMemory(cx);
|
||||
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;
|
||||
|
||||
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,
|
||||
AllocationSiteKey, SystemAllocPolicy>>
|
||||
{
|
||||
|
@ -1412,20 +1427,21 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
|||
{
|
||||
MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
|
||||
MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
|
||||
MOZ_ASSERT(cx->realm() == scriptArg->realm());
|
||||
|
||||
uint32_t offset = scriptArg->pcToOffset(pc);
|
||||
|
||||
if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) {
|
||||
if (offset >= ObjectGroupRealm::AllocationSiteKey::OFFSET_LIMIT) {
|
||||
if (protoArg)
|
||||
return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg));
|
||||
return defaultNewGroup(cx, kind);
|
||||
}
|
||||
|
||||
ObjectGroupCompartment::AllocationSiteTable*& table =
|
||||
cx->compartment()->objectGroups.allocationSiteTable;
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||
ObjectGroupRealm::AllocationSiteTable*& table = realm.allocationSiteTable;
|
||||
|
||||
if (!table) {
|
||||
table = cx->new_<ObjectGroupCompartment::AllocationSiteTable>(cx->zone());
|
||||
table = cx->new_<ObjectGroupRealm::AllocationSiteTable>(cx->zone());
|
||||
if (!table || !table->init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
js_delete(table);
|
||||
|
@ -1442,18 +1458,18 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Rooted<ObjectGroupCompartment::AllocationSiteKey> key(cx,
|
||||
ObjectGroupCompartment::AllocationSiteKey(script, offset, kind, proto));
|
||||
Rooted<ObjectGroupRealm::AllocationSiteKey> key(cx,
|
||||
ObjectGroupRealm::AllocationSiteKey(script, offset, kind, proto));
|
||||
|
||||
ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key);
|
||||
ObjectGroupRealm::AllocationSiteTable::AddPtr p = table->lookupForAdd(key);
|
||||
if (p)
|
||||
return p->value();
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
||||
ObjectGroup* res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged,
|
||||
OBJECT_FLAG_FROM_ALLOCATION_SITE);
|
||||
ObjectGroup* res = ObjectGroupRealm::makeGroup(cx, GetClassForProtoKey(kind), tagged,
|
||||
OBJECT_FLAG_FROM_ALLOCATION_SITE);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1480,9 +1496,11 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
|||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
||||
JSProtoKey kind, ObjectGroup* group)
|
||||
ObjectGroupRealm::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
||||
JSProtoKey kind, ObjectGroup* group)
|
||||
{
|
||||
MOZ_ASSERT(script->realm() == group->realm());
|
||||
|
||||
AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull());
|
||||
|
||||
AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key);
|
||||
|
@ -1590,13 +1608,13 @@ ObjectGroup::findAllocationSite(JSContext* cx, ObjectGroup* group,
|
|||
*script = nullptr;
|
||||
*offset = 0;
|
||||
|
||||
const ObjectGroupCompartment::AllocationSiteTable* table =
|
||||
cx->compartment()->objectGroups.allocationSiteTable;
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::get(group);
|
||||
const ObjectGroupRealm::AllocationSiteTable* table = realm.allocationSiteTable;
|
||||
|
||||
if (!table)
|
||||
return false;
|
||||
|
||||
for (ObjectGroupCompartment::AllocationSiteTable::Range r = table->all();
|
||||
for (ObjectGroupRealm::AllocationSiteTable::Range r = table->all();
|
||||
!r.empty();
|
||||
r.popFront())
|
||||
{
|
||||
|
@ -1611,10 +1629,10 @@ ObjectGroup::findAllocationSite(JSContext* cx, ObjectGroup* group,
|
|||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// ObjectGroupCompartment
|
||||
// ObjectGroupRealm
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
ObjectGroupCompartment::~ObjectGroupCompartment()
|
||||
ObjectGroupRealm::~ObjectGroupRealm()
|
||||
{
|
||||
js_delete(defaultNewTable);
|
||||
js_delete(lazyTable);
|
||||
|
@ -1625,7 +1643,7 @@ ObjectGroupCompartment::~ObjectGroupCompartment()
|
|||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::removeDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
||||
ObjectGroupRealm::removeDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
||||
JSObject* associated)
|
||||
{
|
||||
auto p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated));
|
||||
|
@ -1636,7 +1654,7 @@ ObjectGroupCompartment::removeDefaultNewGroup(const Class* clasp, TaggedProto pr
|
|||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::replaceDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
||||
ObjectGroupRealm::replaceDefaultNewGroup(const Class* clasp, TaggedProto proto,
|
||||
JSObject* associated, ObjectGroup* group)
|
||||
{
|
||||
NewEntry::Lookup lookup(clasp, proto, associated);
|
||||
|
@ -1654,9 +1672,9 @@ ObjectGroupCompartment::replaceDefaultNewGroup(const Class* clasp, TaggedProto p
|
|||
|
||||
/* static */
|
||||
ObjectGroup*
|
||||
ObjectGroupCompartment::makeGroup(JSContext* cx, const Class* clasp,
|
||||
Handle<TaggedProto> proto,
|
||||
ObjectGroupFlags initialFlags /* = 0 */)
|
||||
ObjectGroupRealm::makeGroup(JSContext* cx, const Class* clasp,
|
||||
Handle<TaggedProto> proto,
|
||||
ObjectGroupFlags initialFlags /* = 0 */)
|
||||
{
|
||||
MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
|
||||
|
||||
|
@ -1670,9 +1688,9 @@ ObjectGroupCompartment::makeGroup(JSContext* cx, const Class* clasp,
|
|||
|
||||
/* static */
|
||||
ObjectGroup*
|
||||
ObjectGroupCompartment::getStringSplitStringGroup(JSContext* cx)
|
||||
ObjectGroupRealm::getStringSplitStringGroup(JSContext* cx)
|
||||
{
|
||||
ObjectGroupCompartment& groups = cx->compartment()->objectGroups;
|
||||
ObjectGroupRealm& groups = ObjectGroupRealm::getForNewObject(cx);
|
||||
|
||||
ObjectGroup* group = groups.stringSplitStringGroup.get();
|
||||
if (group) {
|
||||
|
@ -1698,11 +1716,11 @@ ObjectGroupCompartment::getStringSplitStringGroup(JSContext* cx)
|
|||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
ObjectGroupRealm::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||
size_t* allocationSiteTables,
|
||||
size_t* arrayObjectGroupTables,
|
||||
size_t* plainObjectGroupTables,
|
||||
size_t* compartmentTables)
|
||||
size_t* realmTables)
|
||||
{
|
||||
if (allocationSiteTable)
|
||||
*allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
|
||||
|
@ -1726,14 +1744,14 @@ ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeO
|
|||
}
|
||||
|
||||
if (defaultNewTable)
|
||||
*compartmentTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf);
|
||||
*realmTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf);
|
||||
|
||||
if (lazyTable)
|
||||
*compartmentTables += lazyTable->sizeOfIncludingThis(mallocSizeOf);
|
||||
*realmTables += lazyTable->sizeOfIncludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::clearTables()
|
||||
ObjectGroupRealm::clearTables()
|
||||
{
|
||||
if (allocationSiteTable && allocationSiteTable->initialized())
|
||||
allocationSiteTable->clear();
|
||||
|
@ -1756,8 +1774,8 @@ ObjectGroupCompartment::clearTables()
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
ObjectGroupCompartment::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey* key,
|
||||
PlainObjectEntry* entry)
|
||||
ObjectGroupRealm::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey* key,
|
||||
PlainObjectEntry* entry)
|
||||
{
|
||||
if (!(JS::GCPolicy<PlainObjectKey>::needsSweep(key) || entry->needsSweep(key->nproperties)))
|
||||
return false;
|
||||
|
@ -1767,7 +1785,7 @@ ObjectGroupCompartment::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey*
|
|||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::sweep()
|
||||
ObjectGroupRealm::sweep()
|
||||
{
|
||||
/*
|
||||
* Iterate through the array/object group tables and remove all entries
|
||||
|
@ -1786,7 +1804,7 @@ ObjectGroupCompartment::sweep()
|
|||
}
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
|
||||
ObjectGroupRealm::fixupNewTableAfterMovingGC(NewTable* table)
|
||||
{
|
||||
/*
|
||||
* 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
|
||||
|
||||
void
|
||||
ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable* table)
|
||||
ObjectGroupRealm::checkNewTableAfterMovingGC(NewTable* table)
|
||||
{
|
||||
/*
|
||||
* Assert that nothing points into the nursery or needs to be relocated, and
|
||||
|
|
|
@ -29,6 +29,7 @@ class HeapTypeSet;
|
|||
class AutoClearTypeInferenceStateOnOOM;
|
||||
class AutoSweepObjectGroup;
|
||||
class CompilerConstraintList;
|
||||
class ObjectGroupRealm;
|
||||
|
||||
namespace gc {
|
||||
void MergeCompartments(JSCompartment* source, JSCompartment* target);
|
||||
|
@ -527,21 +528,22 @@ class ObjectGroup : public gc::TenuredCell
|
|||
static bool useSingletonForAllocationSite(JSScript* script, jsbytecode* pc,
|
||||
const Class* clasp);
|
||||
|
||||
// Static accessors for ObjectGroupCompartment NewTable.
|
||||
// Static accessors for ObjectGroupRealm NewTable.
|
||||
|
||||
static ObjectGroup* defaultNewGroup(JSContext* cx, const Class* clasp,
|
||||
TaggedProto proto,
|
||||
JSObject* associated = nullptr);
|
||||
static ObjectGroup* lazySingletonGroup(JSContext* cx, const Class* clasp,
|
||||
TaggedProto proto);
|
||||
static ObjectGroup* lazySingletonGroup(JSContext* cx, ObjectGroupRealm& realm,
|
||||
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
|
||||
static bool hasDefaultNewGroup(JSObject* proto, const Class* clasp, ObjectGroup* group);
|
||||
#endif
|
||||
|
||||
// Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable.
|
||||
// Static accessors for ObjectGroupRealm ArrayObjectTable and PlainObjectTable.
|
||||
|
||||
enum class NewArrayKind {
|
||||
Normal, // Specialize array group based on its element type.
|
||||
|
@ -561,7 +563,7 @@ class ObjectGroup : public gc::TenuredCell
|
|||
IdValuePair* properties, size_t nproperties,
|
||||
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
|
||||
// allocation site.
|
||||
|
@ -589,8 +591,8 @@ class ObjectGroup : public gc::TenuredCell
|
|||
static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key);
|
||||
};
|
||||
|
||||
// Structure used to manage the groups in a compartment.
|
||||
class ObjectGroupCompartment
|
||||
// Structure used to manage the groups in a realm.
|
||||
class ObjectGroupRealm
|
||||
{
|
||||
private:
|
||||
class NewTable;
|
||||
|
@ -615,7 +617,7 @@ class ObjectGroupCompartment
|
|||
class AllocationSiteTable;
|
||||
|
||||
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* lazyTable = nullptr;
|
||||
|
||||
|
@ -655,7 +657,7 @@ class ObjectGroupCompartment
|
|||
// Table for referencing types of objects keyed to an allocation site.
|
||||
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
|
||||
// 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
|
||||
|
@ -663,6 +665,10 @@ class ObjectGroupCompartment
|
|||
// on the basis of call-site pc.
|
||||
ReadBarrieredObjectGroup stringSplitStringGroup = {};
|
||||
|
||||
public:
|
||||
// All unboxed layouts in the realm.
|
||||
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
|
||||
|
||||
// END OF PROPERTIES
|
||||
|
||||
private:
|
||||
|
@ -673,8 +679,14 @@ class ObjectGroupCompartment
|
|||
public:
|
||||
struct NewEntry;
|
||||
|
||||
ObjectGroupCompartment() = default;
|
||||
~ObjectGroupCompartment();
|
||||
ObjectGroupRealm() = default;
|
||||
~ObjectGroupRealm();
|
||||
|
||||
ObjectGroupRealm(ObjectGroupRealm&) = delete;
|
||||
void operator=(ObjectGroupRealm&) = delete;
|
||||
|
||||
static ObjectGroupRealm& get(ObjectGroup* group);
|
||||
static ObjectGroupRealm& getForNewObject(JSContext* cx);
|
||||
|
||||
void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
|
||||
JSProtoKey kind, ObjectGroup* group);
|
||||
|
@ -693,7 +705,7 @@ class ObjectGroupCompartment
|
|||
size_t* allocationSiteTables,
|
||||
size_t* arrayGroupTables,
|
||||
size_t* plainObjectGroupTables,
|
||||
size_t* compartmentTables);
|
||||
size_t* realmTables);
|
||||
|
||||
void clearTables();
|
||||
|
||||
|
|
|
@ -64,8 +64,9 @@ ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue pri
|
|||
* ways.
|
||||
*/
|
||||
if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
|
||||
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
|
||||
RootedObject protoObj(cx, proto.toObject());
|
||||
if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
|
||||
if (!JSObject::setNewGroupUnknown(cx, realm, clasp, protoObj))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -1208,16 +1208,16 @@ RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
|||
return n;
|
||||
}
|
||||
|
||||
/* RegExpCompartment */
|
||||
/* RegExpRealm */
|
||||
|
||||
RegExpCompartment::RegExpCompartment()
|
||||
RegExpRealm::RegExpRealm()
|
||||
: matchResultTemplateObject_(nullptr),
|
||||
optimizableRegExpPrototypeShape_(nullptr),
|
||||
optimizableRegExpInstanceShape_(nullptr)
|
||||
{}
|
||||
|
||||
ArrayObject*
|
||||
RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
|
||||
RegExpRealm::createMatchResultTemplateObject(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(!matchResultTemplateObject_);
|
||||
|
||||
|
@ -1229,7 +1229,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
|
|||
|
||||
// Create a new group for the template.
|
||||
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
|
||||
ObjectGroup* group = ObjectGroupRealm::makeGroup(cx, templateObject->getClass(), proto);
|
||||
if (!group)
|
||||
return matchResultTemplateObject_; // = nullptr
|
||||
templateObject->setGroup(group);
|
||||
|
@ -1274,7 +1274,7 @@ RegExpZone::init()
|
|||
}
|
||||
|
||||
void
|
||||
RegExpCompartment::sweep()
|
||||
RegExpRealm::sweep()
|
||||
{
|
||||
if (matchResultTemplateObject_ &&
|
||||
IsAboutToBeFinalized(&matchResultTemplateObject_))
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
namespace js {
|
||||
|
||||
class ArrayObject;
|
||||
class RegExpCompartment;
|
||||
class RegExpRealm;
|
||||
class RegExpShared;
|
||||
class RegExpStatics;
|
||||
class VectorMatchPairs;
|
||||
|
@ -288,7 +288,7 @@ class RegExpZone
|
|||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
};
|
||||
|
||||
class RegExpCompartment
|
||||
class RegExpRealm
|
||||
{
|
||||
/*
|
||||
* This is the template object where the result of re.exec() is based on,
|
||||
|
@ -321,7 +321,7 @@ class RegExpCompartment
|
|||
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
|
||||
|
||||
public:
|
||||
explicit RegExpCompartment();
|
||||
explicit RegExpRealm();
|
||||
|
||||
void sweep();
|
||||
|
||||
|
@ -346,10 +346,10 @@ class RegExpCompartment
|
|||
}
|
||||
|
||||
static size_t offsetOfOptimizableRegExpPrototypeShape() {
|
||||
return offsetof(RegExpCompartment, optimizableRegExpPrototypeShape_);
|
||||
return offsetof(RegExpRealm, optimizableRegExpPrototypeShape_);
|
||||
}
|
||||
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;
|
||||
|
||||
gc.atomsZone = atomsZone.get();
|
||||
if (!atomsZone->compartments().append(atomsRealm.get()))
|
||||
if (!atomsZone->compartments().append(JS::GetCompartmentForRealm(atomsRealm.get())))
|
||||
return false;
|
||||
|
||||
atomsRealm->setIsSystem(true);
|
||||
|
|
|
@ -101,21 +101,21 @@ LiveSavedFrameCache::find(JSContext* cx, FramePtr& framePtr, const jsbytecode* p
|
|||
MOZ_ASSERT(initialized());
|
||||
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.
|
||||
if (frames->empty()) {
|
||||
frame.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// All our SavedFrames should be in the same compartment. If the last
|
||||
// entry's SavedFrame's compartment doesn't match cx's, flush the cache.
|
||||
if (frames->back().savedFrame->compartment() != cx->compartment()) {
|
||||
// All our SavedFrames should be in the same realm. If the last
|
||||
// entry's SavedFrame's realm doesn't match cx's, flush the cache.
|
||||
if (frames->back().savedFrame->realm() != cx->realm()) {
|
||||
#ifdef DEBUG
|
||||
// Check that they are, indeed, all in the same compartment.
|
||||
auto compartment = frames->back().savedFrame->compartment();
|
||||
// Check that they are, indeed, all in the same realm.
|
||||
auto compartment = frames->back().savedFrame->realm();
|
||||
for (const auto& f : (*frames))
|
||||
MOZ_ASSERT(compartment == f.savedFrame->compartment());
|
||||
MOZ_ASSERT(compartment == f.savedFrame->realm());
|
||||
#endif
|
||||
frames->clear();
|
||||
frame.set(nullptr);
|
||||
|
@ -124,7 +124,7 @@ LiveSavedFrameCache::find(JSContext* cx, FramePtr& framePtr, const jsbytecode* p
|
|||
|
||||
Key key(framePtr);
|
||||
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,
|
||||
// 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
|
||||
// `SavedStacksMetadataBuilder` for this new SavedFrame object, and
|
||||
// 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));
|
||||
if (!proto)
|
||||
|
@ -829,7 +829,7 @@ GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
{
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
|
@ -852,7 +852,7 @@ GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
MOZ_ASSERT(linep);
|
||||
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
|
@ -872,7 +872,7 @@ GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
MOZ_ASSERT(columnp);
|
||||
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
|
@ -892,7 +892,7 @@ GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, Mutable
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
{
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
|
@ -915,7 +915,7 @@ GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleStr
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
{
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
|
@ -946,7 +946,7 @@ GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleOb
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
|
@ -979,7 +979,7 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
AutoMaybeEnterFrameRealm ar(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
|
@ -1085,7 +1085,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
|
|||
{
|
||||
js::AssertHeapIsIdle();
|
||||
CHECK_REQUEST(cx);
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
|
||||
js::StringBuffer sb(cx);
|
||||
|
||||
|
@ -1279,8 +1279,8 @@ SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
|
|||
JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
|
||||
{
|
||||
MOZ_ASSERT(initialized());
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
assertSameCompartment(cx, this);
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
|
||||
|
||||
if (creatingSavedFrame ||
|
||||
cx->isExceptionPending() ||
|
||||
|
@ -1301,8 +1301,8 @@ SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString
|
|||
const Maybe<size_t>& maxFrameCount)
|
||||
{
|
||||
MOZ_ASSERT(initialized());
|
||||
MOZ_RELEASE_ASSERT(cx->compartment());
|
||||
assertSameCompartment(cx, this);
|
||||
MOZ_RELEASE_ASSERT(cx->realm());
|
||||
MOZ_DIAGNOSTIC_ASSERT(&cx->realm()->savedStacks() == this);
|
||||
|
||||
RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
|
||||
if (!asyncCauseAtom)
|
||||
|
@ -1475,7 +1475,7 @@ SavedStacks::insertFrames(JSContext* cx, MutableHandleSavedFrame frame,
|
|||
Rooted<LocationValue> location(cx);
|
||||
{
|
||||
AutoRealmUnchecked ar(cx, iter.compartment());
|
||||
if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
|
||||
if (!cx->realm()->savedStacks().getLocation(cx, iter, &location))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1618,12 +1618,12 @@ SavedStacks::adoptAsyncStack(JSContext* cx, MutableHandleSavedFrame asyncStack,
|
|||
// Attach the asyncCause to the youngest frame.
|
||||
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
|
||||
// just use the existing chain. Only the asyncCause on the youngest frame
|
||||
// needs to be changed.
|
||||
if (currentSavedFrame == nullptr &&
|
||||
asyncStack->compartment() == cx->compartment())
|
||||
asyncStack->realm() == cx->realm())
|
||||
{
|
||||
SavedFrame::HandleLookup lookup = stackChain[0];
|
||||
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
|
||||
// the cache because our compartment's sweep method isn't called when their
|
||||
// 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
|
||||
// location from our PCLocationMap and copy it into |locationp|. When we do
|
||||
|
@ -1806,9 +1807,8 @@ SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
|
|||
}
|
||||
|
||||
void
|
||||
SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
|
||||
SavedStacks::chooseSamplingProbability(Realm* realm)
|
||||
{
|
||||
Realm* realm = JS::GetRealmForCompartment(compartment);
|
||||
GlobalObject* global = realm->maybeGlobal();
|
||||
if (!global)
|
||||
return;
|
||||
|
@ -1850,7 +1850,7 @@ SavedStacks::MetadataBuilder::build(JSContext* cx, HandleObject target,
|
|||
{
|
||||
RootedObject obj(cx, target);
|
||||
|
||||
SavedStacks& stacks = cx->compartment()->savedStacks();
|
||||
SavedStacks& stacks = cx->realm()->savedStacks();
|
||||
if (!stacks.bernoulli.trial())
|
||||
return nullptr;
|
||||
|
||||
|
@ -1867,18 +1867,6 @@ SavedStacks::MetadataBuilder::build(JSContext* cx, HandleObject target,
|
|||
|
||||
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::IsNotSystem;
|
||||
|
||||
|
@ -2001,7 +1989,7 @@ ConstructSavedFrameStackSlow(JSContext* cx, JS::ubi::StackFrame& frame,
|
|||
for (size_t i = stackChain->length(); i != 0; i--) {
|
||||
SavedFrame::HandleLookup lookup = stackChain[i-1];
|
||||
lookup->parent = parentFrame;
|
||||
parentFrame = cx->compartment()->savedStacks().getOrCreateSavedFrame(cx, lookup);
|
||||
parentFrame = cx->realm()->savedStacks().getOrCreateSavedFrame(cx, lookup);
|
||||
if (!parentFrame)
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ class SavedStacks {
|
|||
void trace(JSTracer* trc);
|
||||
uint32_t count();
|
||||
void clear();
|
||||
void chooseSamplingProbability(JSCompartment*);
|
||||
void chooseSamplingProbability(JS::Realm* realm);
|
||||
|
||||
// Set the sampling random number generator's state to |state0| and
|
||||
// |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 sep(cx, args[1].toString());
|
||||
|
||||
RootedObjectGroup group(cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
|
||||
RootedObjectGroup group(cx, ObjectGroupRealm::getStringSplitStringGroup(cx));
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
|
@ -1790,7 +1790,7 @@ intrinsic_StringSplitStringLimit(JSContext* cx, unsigned argc, Value* vp)
|
|||
uint32_t limit = uint32_t(args[2].toNumber());
|
||||
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)
|
||||
return false;
|
||||
|
||||
|
@ -2809,7 +2809,7 @@ JSRuntime::createSelfHostingGlobal(JSContext* cx)
|
|||
&shgClassOps
|
||||
};
|
||||
|
||||
AutoRealmUnchecked ar(cx, realm);
|
||||
AutoRealmUnchecked ar(cx, compartment);
|
||||
Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
|
||||
if (!shg)
|
||||
return nullptr;
|
||||
|
|
|
@ -385,8 +385,8 @@ InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc)
|
|||
traceValues(trc, 0, nlivefixed);
|
||||
}
|
||||
|
||||
if (script->compartment()->debugEnvs)
|
||||
script->compartment()->debugEnvs->traceLiveFrame(trc, this);
|
||||
if (auto* debugEnvs = script->realm()->debugEnvs())
|
||||
debugEnvs->traceLiveFrame(trc, this);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче