mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-12 17:31:05 +00:00
commit
cfcbf3f6e4
|
@ -1013,7 +1013,7 @@ pref("browser.sessionstore.resume_from_crash", true);
|
|||
pref("browser.sessionstore.resume_session_once", false);
|
||||
|
||||
// minimal interval between two save operations in milliseconds
|
||||
pref("browser.sessionstore.interval", 25000);
|
||||
pref("browser.sessionstore.interval", 30000);
|
||||
// on which sites to save text data, POSTDATA and cookies
|
||||
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
|
||||
pref("browser.sessionstore.privacy_level", 0);
|
||||
|
@ -1026,9 +1026,9 @@ pref("browser.sessionstore.max_windows_undo", 2);
|
|||
// (this pref has no effect if more than 6 hours have passed since the last crash)
|
||||
pref("browser.sessionstore.max_resumed_crashes", 1);
|
||||
// number of back button session history entries to restore (-1 = all of them)
|
||||
pref("browser.sessionstore.max_serialize_back", 10);
|
||||
pref("browser.sessionstore.max_serialize_back", 5);
|
||||
// number of forward button session history entries to restore (-1 = all of them)
|
||||
pref("browser.sessionstore.max_serialize_forward", -1);
|
||||
pref("browser.sessionstore.max_serialize_forward", 5);
|
||||
// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant)
|
||||
// and restore_hidden_tabs. When true, tabs will not be restored until they are
|
||||
// focused (also applies to tabs that aren't visible). When false, the values
|
||||
|
@ -1051,8 +1051,8 @@ pref("browser.sessionstore.debug", false);
|
|||
// browser tabs that were not caused by a flush from the parent.
|
||||
// This is a testing flag and should not be used by end-users.
|
||||
pref("browser.sessionstore.debug.no_auto_updates", false);
|
||||
// Forget closed windows/tabs after two weeks
|
||||
pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
|
||||
// Forget closed windows/tabs after 24 hours
|
||||
pref("browser.sessionstore.cleanup.forget_closed_after", 86400000);
|
||||
|
||||
// allow META refresh by default
|
||||
pref("accessibility.blockautorefresh", false);
|
||||
|
|
|
@ -506,7 +506,7 @@ var SessionStoreInternal = {
|
|||
throw new Error("SessionStore.init() must only be called once!");
|
||||
}
|
||||
|
||||
TelemetryTimestamps.add("sessionRestoreInitialized");
|
||||
//TelemetryTimestamps.add("sessionRestoreInitialized");
|
||||
OBSERVING.forEach(function(aTopic) {
|
||||
Services.obs.addObserver(this, aTopic, true);
|
||||
}, this);
|
||||
|
@ -519,7 +519,7 @@ var SessionStoreInternal = {
|
|||
* Initialize the session using the state provided by SessionStartup
|
||||
*/
|
||||
initSession: function () {
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS");
|
||||
//TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS");
|
||||
let state;
|
||||
let ss = gSessionStartup;
|
||||
|
||||
|
@ -593,7 +593,7 @@ var SessionStoreInternal = {
|
|||
this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
|
||||
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
|
||||
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS");
|
||||
//TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS");
|
||||
return state;
|
||||
},
|
||||
|
||||
|
@ -743,7 +743,7 @@ var SessionStoreInternal = {
|
|||
|
||||
// Record telemetry measurements done in the child and update the tab's
|
||||
// cached state. Mark the window as dirty and trigger a delayed write.
|
||||
this.recordTelemetry(aMessage.data.telemetry);
|
||||
//this.recordTelemetry(aMessage.data.telemetry);
|
||||
TabState.update(browser, aMessage.data);
|
||||
this.saveStateDelayed(win);
|
||||
|
||||
|
@ -874,9 +874,11 @@ var SessionStoreInternal = {
|
|||
* value to be recorded for that ID,
|
||||
*/
|
||||
recordTelemetry: function (telemetry) {
|
||||
/*
|
||||
for (let histogramId in telemetry){
|
||||
Telemetry.getHistogramById(histogramId).add(telemetry[histogramId]);
|
||||
}
|
||||
*/
|
||||
},
|
||||
|
||||
/* ........ Window Event Handlers .............. */
|
||||
|
@ -1025,7 +1027,7 @@ var SessionStoreInternal = {
|
|||
// Nothing to restore now, notify observers things are complete.
|
||||
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||
} else {
|
||||
TelemetryTimestamps.add("sessionRestoreRestoring");
|
||||
//TelemetryTimestamps.add("sessionRestoreRestoring");
|
||||
this._restoreCount = aInitialState.windows ? aInitialState.windows.length : 0;
|
||||
|
||||
// global data must be restored before restoreWindow is called so that
|
||||
|
@ -1188,9 +1190,9 @@ var SessionStoreInternal = {
|
|||
if (initialState) {
|
||||
Services.obs.notifyObservers(null, NOTIFY_RESTORING_ON_STARTUP, "");
|
||||
}
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
|
||||
//TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
|
||||
this.initializeWindow(aWindow, initialState);
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
|
||||
//TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
|
||||
|
||||
// Let everyone know we're done.
|
||||
this._deferredInitialized.resolve();
|
||||
|
@ -2675,7 +2677,7 @@ var SessionStoreInternal = {
|
|||
|
||||
var activeWindow = this._getMostRecentBrowserWindow();
|
||||
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");
|
||||
//TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");
|
||||
if (RunState.isRunning) {
|
||||
// update the data for all windows with activities since the last save operation
|
||||
this._forEachBrowserWindow(function(aWindow) {
|
||||
|
@ -2690,7 +2692,7 @@ var SessionStoreInternal = {
|
|||
});
|
||||
DirtyWindows.clear();
|
||||
}
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");
|
||||
//TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_ALL_WINDOWS_DATA_MS");
|
||||
|
||||
// An array that at the end will hold all current window data.
|
||||
var total = [];
|
||||
|
@ -2710,9 +2712,9 @@ var SessionStoreInternal = {
|
|||
nonPopupCount++;
|
||||
}
|
||||
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
|
||||
//TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
|
||||
SessionCookies.update(total);
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
|
||||
//TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
|
||||
|
||||
// collect the data for all windows yet to be restored
|
||||
for (ix in this._statesToRestore) {
|
||||
|
@ -2877,7 +2879,7 @@ var SessionStoreInternal = {
|
|||
if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
|
||||
this.onLoad(aWindow);
|
||||
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
//TelemetryStopwatch.start("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
|
||||
// We're not returning from this before we end up calling restoreTabs
|
||||
// for this window, so make sure we send the SSWindowStateBusy event.
|
||||
|
@ -3031,7 +3033,7 @@ var SessionStoreInternal = {
|
|||
// set smoothScroll back to the original value
|
||||
tabstrip.smoothScroll = smoothScroll;
|
||||
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
//TelemetryStopwatch.finish("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
|
||||
this._setWindowStateReady(aWindow);
|
||||
|
||||
|
@ -4146,6 +4148,7 @@ var SessionStoreInternal = {
|
|||
* Handle an error report from a content process.
|
||||
*/
|
||||
reportInternalError(data) {
|
||||
/*
|
||||
// For the moment, we only report errors through Telemetry.
|
||||
if (data.telemetry) {
|
||||
for (let key of Object.keys(data.telemetry)) {
|
||||
|
@ -4153,6 +4156,7 @@ var SessionStoreInternal = {
|
|||
histogram.add(data.telemetry[key]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -399,10 +399,10 @@ endif # WINNT && !GNU_CC
|
|||
ifeq ($(OS_ARCH),Darwin)
|
||||
# Compiling ObjC requires an Apple compiler anyway, so it's ok to set
|
||||
# host CMFLAGS here.
|
||||
HOST_CMFLAGS += -fobjc-exceptions -mno-altivec -mabi=no-altivec
|
||||
HOST_CMMFLAGS += -fobjc-exceptions -mno-altivec -mabi=no-altivec
|
||||
OS_COMPILE_CMFLAGS += -fobjc-exceptions -mno-altivec -mabi=no-altivec
|
||||
OS_COMPILE_CMMFLAGS += -fobjc-exceptions -mno-altivec -mabi=no-altivec
|
||||
HOST_CMFLAGS += -fobjc-exceptions
|
||||
HOST_CMMFLAGS += -fobjc-exceptions
|
||||
OS_COMPILE_CMFLAGS += -fobjc-exceptions
|
||||
OS_COMPILE_CMMFLAGS += -fobjc-exceptions
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),uikit)
|
||||
OS_COMPILE_CMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
|
||||
OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
|
||||
|
|
|
@ -8382,7 +8382,11 @@ nsDocument::AddToRadioGroup(const nsAString& aName,
|
|||
|
||||
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
|
||||
NS_ASSERTION(element, "radio controls have to be content elements");
|
||||
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
|
||||
HTMLInputElement* input = HTMLInputElement::FromContent(element);
|
||||
NS_ASSERTION(input, "radio controls have to be input elements!");
|
||||
|
||||
if (input->IsRequired()) {
|
||||
radioGroup->mRequiredRadioCount++;
|
||||
}
|
||||
}
|
||||
|
@ -8396,7 +8400,11 @@ nsDocument::RemoveFromRadioGroup(const nsAString& aName,
|
|||
|
||||
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
|
||||
NS_ASSERTION(element, "radio controls have to be content elements");
|
||||
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
|
||||
HTMLInputElement* input = HTMLInputElement::FromContent(element);
|
||||
NS_ASSERTION(input, "radio controls have to be input elements!");
|
||||
|
||||
if (input->IsRequired()) {
|
||||
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
|
||||
"mRequiredRadioCount about to wrap below 0!");
|
||||
radioGroup->mRequiredRadioCount--;
|
||||
|
|
|
@ -976,7 +976,7 @@ nsGenericDOMDataNode::GetText()
|
|||
uint32_t
|
||||
nsGenericDOMDataNode::TextLength() const
|
||||
{
|
||||
return mText.GetLength();
|
||||
return TextDataLength();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -219,6 +219,11 @@ public:
|
|||
rv = ReplaceData(aOffset, aCount, aData);
|
||||
}
|
||||
|
||||
uint32_t TextDataLength() const
|
||||
{
|
||||
return mText.GetLength();
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -2396,9 +2396,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
// transplanting code, since it has no good way to handle errors. This uses
|
||||
// the untrusted script limit, which is not strictly necessary since no
|
||||
// actual script should run.
|
||||
bool overrecursed = false;
|
||||
JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, overrecursed = true);
|
||||
if (overrecursed) {
|
||||
if (MOZ_UNLIKELY(!js::CheckRecursionConservativeDontReport(cx))) {
|
||||
NS_WARNING("Overrecursion in SetNewDocument");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -1650,6 +1650,168 @@ nsINode::GetNextElementSibling() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsINode>
|
||||
GetNodeFromNodeOrString(const OwningNodeOrString& aNode,
|
||||
nsIDocument* aDocument)
|
||||
{
|
||||
if (aNode.IsNode()) {
|
||||
nsCOMPtr<nsINode> node = aNode.GetAsNode();
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
if (aNode.IsString()){
|
||||
RefPtr<nsTextNode> textNode =
|
||||
aDocument->CreateTextNode(aNode.GetAsString());
|
||||
return textNode.forget();
|
||||
}
|
||||
|
||||
MOZ_CRASH("Impossible type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the algorithm specified at
|
||||
* https://dom.spec.whatwg.org/#converting-nodes-into-a-node for |prepend()|,
|
||||
* |append()|, |before()|, |after()|, and |replaceWith()| APIs.
|
||||
*/
|
||||
static already_AddRefed<nsINode>
|
||||
ConvertNodesOrStringsIntoNode(const Sequence<OwningNodeOrString>& aNodes,
|
||||
nsIDocument* aDocument,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aNodes.Length() == 1) {
|
||||
return GetNodeFromNodeOrString(aNodes[0], aDocument);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> fragment = aDocument->CreateDocumentFragment();
|
||||
|
||||
for (const auto& node : aNodes) {
|
||||
nsCOMPtr<nsINode> childNode = GetNodeFromNodeOrString(node, aDocument);
|
||||
fragment->AppendChild(*childNode, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return fragment.forget();
|
||||
}
|
||||
|
||||
static void
|
||||
InsertNodesIntoHashset(const Sequence<OwningNodeOrString>& aNodes,
|
||||
nsTHashtable<nsPtrHashKey<nsINode>>& aHashset)
|
||||
{
|
||||
for (const auto& node : aNodes) {
|
||||
if (node.IsNode()) {
|
||||
aHashset.PutEntry(node.GetAsNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static nsINode*
|
||||
FindViablePreviousSibling(const nsINode& aNode,
|
||||
const Sequence<OwningNodeOrString>& aNodes)
|
||||
{
|
||||
nsTHashtable<nsPtrHashKey<nsINode>> nodeSet(16);
|
||||
InsertNodesIntoHashset(aNodes, nodeSet);
|
||||
|
||||
nsINode* viablePreviousSibling = nullptr;
|
||||
for (nsINode* sibling = aNode.GetPreviousSibling(); sibling;
|
||||
sibling = sibling->GetPreviousSibling()) {
|
||||
if (!nodeSet.Contains(sibling)) {
|
||||
viablePreviousSibling = sibling;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return viablePreviousSibling;
|
||||
}
|
||||
|
||||
static nsINode*
|
||||
FindViableNextSibling(const nsINode& aNode,
|
||||
const Sequence<OwningNodeOrString>& aNodes)
|
||||
{
|
||||
nsTHashtable<nsPtrHashKey<nsINode>> nodeSet(16);
|
||||
InsertNodesIntoHashset(aNodes, nodeSet);
|
||||
|
||||
nsINode* viableNextSibling = nullptr;
|
||||
for (nsINode* sibling = aNode.GetNextSibling(); sibling;
|
||||
sibling = sibling->GetNextSibling()) {
|
||||
if (!nodeSet.Contains(sibling)) {
|
||||
viableNextSibling = sibling;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return viableNextSibling;
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::Before(const Sequence<OwningNodeOrString>& aNodes,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsINode> parent = GetParentNode();
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> viablePreviousSibling =
|
||||
FindViablePreviousSibling(*this, aNodes);
|
||||
|
||||
nsCOMPtr<nsINode> node =
|
||||
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
viablePreviousSibling = viablePreviousSibling ?
|
||||
viablePreviousSibling->GetNextSibling() : parent->GetFirstChild();
|
||||
|
||||
parent->InsertBefore(*node, viablePreviousSibling, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::After(const Sequence<OwningNodeOrString>& aNodes,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsINode> parent = GetParentNode();
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
|
||||
|
||||
nsCOMPtr<nsINode> node =
|
||||
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent->InsertBefore(*node, viableNextSibling, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsINode> parent = GetParentNode();
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
|
||||
|
||||
nsCOMPtr<nsINode> node =
|
||||
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent == GetParentNode()) {
|
||||
parent->ReplaceChild(*node, *this, aRv);
|
||||
} else {
|
||||
parent->InsertBefore(*node, viableNextSibling, aRv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::Remove()
|
||||
{
|
||||
|
@ -1693,6 +1855,33 @@ nsINode::GetLastElementChild() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsINode> node =
|
||||
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> refNode = mFirstChild;
|
||||
InsertBefore(*node, refNode, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::Append(const Sequence<OwningNodeOrString>& aNodes,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsINode> node =
|
||||
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppendChild(*node, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
|
||||
nsIContent* aKid, nsAttrAndChildArray& aChildArray)
|
||||
|
|
|
@ -75,6 +75,8 @@ class DOMRectReadOnly;
|
|||
class Element;
|
||||
class EventHandlerNonNull;
|
||||
template<typename T> class Optional;
|
||||
class OwningNodeOrString;
|
||||
template<typename> class Sequence;
|
||||
class Text;
|
||||
class TextOrElementOrDocument;
|
||||
struct DOMPointInit;
|
||||
|
@ -267,9 +269,13 @@ public:
|
|||
typedef mozilla::dom::DOMPointInit DOMPointInit;
|
||||
typedef mozilla::dom::DOMQuad DOMQuad;
|
||||
typedef mozilla::dom::DOMRectReadOnly DOMRectReadOnly;
|
||||
typedef mozilla::dom::OwningNodeOrString OwningNodeOrString;
|
||||
typedef mozilla::dom::TextOrElementOrDocument TextOrElementOrDocument;
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
|
||||
template<class T>
|
||||
using Sequence = mozilla::dom::Sequence<T>;
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
|
||||
|
||||
// Among the sub-classes that inherit (directly or indirectly) from nsINode,
|
||||
|
@ -1807,6 +1813,11 @@ public:
|
|||
// ChildNode methods
|
||||
mozilla::dom::Element* GetPreviousElementSibling() const;
|
||||
mozilla::dom::Element* GetNextElementSibling() const;
|
||||
|
||||
void Before(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
|
||||
void After(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
|
||||
void ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
|
||||
ErrorResult& aRv);
|
||||
/**
|
||||
* Remove this node from its parent, if any.
|
||||
*/
|
||||
|
@ -1816,6 +1827,9 @@ public:
|
|||
mozilla::dom::Element* GetFirstElementChild() const;
|
||||
mozilla::dom::Element* GetLastElementChild() const;
|
||||
|
||||
void Prepend(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
|
||||
void Append(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
|
||||
|
||||
void GetBoxQuads(const BoxQuadOptions& aOptions,
|
||||
nsTArray<RefPtr<DOMQuad> >& aResult,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
|
|
@ -1584,15 +1584,11 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// sync request is not allowed using withCredential or responseType
|
||||
// sync request is not allowed to use responseType or timeout
|
||||
// in window context
|
||||
if (!async && HasOrHasHadOwner() &&
|
||||
(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
|
||||
mTimeoutMilliseconds ||
|
||||
(mTimeoutMilliseconds ||
|
||||
mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
|
||||
if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
|
||||
LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
|
||||
}
|
||||
if (mTimeoutMilliseconds) {
|
||||
LogMessage("TimeoutSyncXHRWarning", GetOwner());
|
||||
}
|
||||
|
@ -3262,14 +3258,6 @@ nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
|
|||
return;
|
||||
}
|
||||
|
||||
// sync request is not allowed setting withCredentials in window context
|
||||
if (HasOrHasHadOwner() &&
|
||||
!(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
|
||||
LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aWithCredentials) {
|
||||
mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
|
||||
} else {
|
||||
|
|
|
@ -769,7 +769,8 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Handle<JSObject*> parentProto,
|
||||
const js::Class* protoClass,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties)
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* const* unscopableNames)
|
||||
{
|
||||
JS::Rooted<JSObject*> ourProto(cx,
|
||||
JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto));
|
||||
|
@ -778,6 +779,28 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (unscopableNames) {
|
||||
JS::Rooted<JSObject*> unscopableObj(cx, JS_NewPlainObject(cx));
|
||||
if (!unscopableObj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (; *unscopableNames; ++unscopableNames) {
|
||||
if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
|
||||
JS::TrueHandleValue, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<jsid> unscopableId(cx,
|
||||
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::unscopables)));
|
||||
// Readonly and non-enumerable to match Array.prototype.
|
||||
if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
|
||||
JSPROP_READONLY)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ourProto;
|
||||
}
|
||||
|
||||
|
@ -833,7 +856,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Heap<JSObject*>* constructorCache,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name, bool defineOnGlobal)
|
||||
const char* name, bool defineOnGlobal,
|
||||
const char* const* unscopableNames)
|
||||
{
|
||||
MOZ_ASSERT(protoClass || constructorClass || constructor,
|
||||
"Need at least one class or a constructor!");
|
||||
|
@ -864,7 +888,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
if (protoClass) {
|
||||
proto =
|
||||
CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
|
||||
properties, chromeOnlyProperties);
|
||||
properties, chromeOnlyProperties,
|
||||
unscopableNames);
|
||||
if (!proto) {
|
||||
return;
|
||||
}
|
||||
|
@ -1891,7 +1916,9 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
|||
// transplanting code, since it has no good way to handle errors. This uses
|
||||
// the untrusted script limit, which is not strictly necessary since no
|
||||
// actual script should run.
|
||||
JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
|
||||
if (MOZ_UNLIKELY(!js::CheckRecursionConservative(aCx))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> aObj(aCx, aObjArg);
|
||||
const DOMJSClass* domClass = GetDOMClass(aObj);
|
||||
|
|
|
@ -611,6 +611,8 @@ struct NamedConstructor
|
|||
* false in situations where we want the properties to only
|
||||
* appear on privileged Xrays but not on the unprivileged
|
||||
* underlying global.
|
||||
* unscopableNames if not null it points to a null-terminated list of const
|
||||
* char* names of the unscopable properties for this interface.
|
||||
*
|
||||
* At least one of protoClass, constructorClass or constructor should be
|
||||
* non-null. If constructorClass or constructor are non-null, the resulting
|
||||
|
@ -627,7 +629,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Heap<JSObject*>* constructorCache,
|
||||
const NativeProperties* regularProperties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name, bool defineOnGlobal);
|
||||
const char* name, bool defineOnGlobal,
|
||||
const char* const* unscopableNames);
|
||||
|
||||
/**
|
||||
* Define the properties (regular and chrome-only) on obj.
|
||||
|
|
|
@ -2691,13 +2691,14 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
|
||||
properties should be a PropertyArrays instance.
|
||||
"""
|
||||
def __init__(self, descriptor, properties):
|
||||
def __init__(self, descriptor, properties, haveUnscopables):
|
||||
args = [Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'aGlobal'),
|
||||
Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
|
||||
Argument('bool', 'aDefineOnGlobal')]
|
||||
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
|
||||
self.properties = properties
|
||||
self.haveUnscopables = haveUnscopables
|
||||
|
||||
def definition_body(self):
|
||||
(protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor)
|
||||
|
@ -2837,7 +2838,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
interfaceCache,
|
||||
${properties},
|
||||
${chromeProperties},
|
||||
${name}, aDefineOnGlobal);
|
||||
${name}, aDefineOnGlobal,
|
||||
${unscopableNames});
|
||||
""",
|
||||
protoClass=protoClass,
|
||||
parentProto=parentProto,
|
||||
|
@ -2849,7 +2851,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
interfaceCache=interfaceCache,
|
||||
properties=properties,
|
||||
chromeProperties=chromeProperties,
|
||||
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr")
|
||||
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
|
||||
unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr")
|
||||
|
||||
# If we fail after here, we must clear interface and prototype caches
|
||||
# using this code: intermediate failure must not expose the interface in
|
||||
|
@ -11762,6 +11765,7 @@ class CGDescriptor(CGThing):
|
|||
hasPromiseReturningMethod) = False, False, False, False, False, False
|
||||
jsonifierMethod = None
|
||||
crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
|
||||
unscopableNames = list()
|
||||
for n in descriptor.interface.namedConstructors:
|
||||
cgThings.append(CGClassConstructor(descriptor, n,
|
||||
NamedConstructorName(n)))
|
||||
|
@ -11774,6 +11778,9 @@ class CGDescriptor(CGThing):
|
|||
props = memberProperties(m, descriptor)
|
||||
|
||||
if m.isMethod():
|
||||
if m.getExtendedAttribute("Unscopable"):
|
||||
assert not m.isStatic()
|
||||
unscopableNames.append(m.identifier.name)
|
||||
if props.isJsonifier:
|
||||
jsonifierMethod = m
|
||||
elif not m.isIdentifierLess() or m == descriptor.operations['Stringifier']:
|
||||
|
@ -11799,6 +11806,9 @@ class CGDescriptor(CGThing):
|
|||
raise TypeError("Stringifier attributes not supported yet. "
|
||||
"See bug 824857.\n"
|
||||
"%s" % m.location)
|
||||
if m.getExtendedAttribute("Unscopable"):
|
||||
assert not m.isStatic()
|
||||
unscopableNames.append(m.identifier.name)
|
||||
if m.isStatic():
|
||||
assert descriptor.interface.hasInterfaceObject()
|
||||
cgThings.append(CGStaticGetter(descriptor, m))
|
||||
|
@ -11986,9 +11996,20 @@ class CGDescriptor(CGThing):
|
|||
cgThings.extend(CGClearCachedValueMethod(descriptor, m) for
|
||||
m in clearableCachedAttrs(descriptor))
|
||||
|
||||
haveUnscopables = (len(unscopableNames) != 0 and
|
||||
descriptor.interface.hasInterfacePrototypeObject())
|
||||
if haveUnscopables:
|
||||
cgThings.append(
|
||||
CGList([CGGeneric("static const char* const unscopableNames[] = {"),
|
||||
CGIndenter(CGList([CGGeneric('"%s"' % name) for
|
||||
name in unscopableNames] +
|
||||
[CGGeneric("nullptr")], ",\n")),
|
||||
CGGeneric("};\n")], "\n"))
|
||||
|
||||
# CGCreateInterfaceObjectsMethod needs to come after our
|
||||
# CGDOMJSClass, if any.
|
||||
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
|
||||
# CGDOMJSClass and unscopables, if any.
|
||||
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties,
|
||||
haveUnscopables))
|
||||
|
||||
# CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
|
||||
# to come after CGCreateInterfaceObjectsMethod.
|
||||
|
|
|
@ -4011,6 +4011,14 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
raise WebIDLError("[UseCounter] must not be used on a "
|
||||
"stringifier attribute",
|
||||
[attr.location, self.location])
|
||||
elif identifier == "Unscopable":
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[Unscopable] must take no arguments",
|
||||
[attr.location])
|
||||
if self.isStatic():
|
||||
raise WebIDLError("[Unscopable] is only allowed on non-static "
|
||||
"attributes and operations",
|
||||
[attr.location, self.location])
|
||||
elif (identifier == "Pref" or
|
||||
identifier == "Deprecated" or
|
||||
identifier == "SetterThrows" or
|
||||
|
@ -4711,6 +4719,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
raise WebIDLError("[UseCounter] must not be used on a special "
|
||||
"operation",
|
||||
[attr.location, self.location])
|
||||
elif identifier == "Unscopable":
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[Unscopable] must take no arguments",
|
||||
[attr.location])
|
||||
if self.isStatic():
|
||||
raise WebIDLError("[Unscopable] is only allowed on non-static "
|
||||
"attributes and operations",
|
||||
[attr.location, self.location])
|
||||
elif (identifier == "Throws" or
|
||||
identifier == "NewObject" or
|
||||
identifier == "ChromeOnly" or
|
||||
|
|
|
@ -522,6 +522,14 @@ Event::PreventDefaultInternal(bool aCalledByDefaultHandler)
|
|||
if (!mEvent->mFlags.mCancelable) {
|
||||
return;
|
||||
}
|
||||
if (mEvent->mFlags.mInPassiveListener) {
|
||||
#if DEBUG
|
||||
// XXX: There should be a warning here, but we don't have the string.
|
||||
// https://hg.mozilla.org/mozilla-central/rev/cda76e80a47c
|
||||
fprintf(stderr, "** PreventDefaultInternal called on Passive Listener\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
mEvent->mFlags.mDefaultPrevented = true;
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ EventListenerManager::AddEventListenerInternal(
|
|||
listener = &mListeners.ElementAt(i);
|
||||
// mListener == aListenerHolder is the last one, since it can be a bit slow.
|
||||
if (listener->mListenerIsHandler == aHandler &&
|
||||
listener->mFlags == aFlags &&
|
||||
listener->mFlags.EqualsForAddition(aFlags) &&
|
||||
EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, aTypeString,
|
||||
aAllEvents) &&
|
||||
listener->mListener == aListenerHolder) {
|
||||
|
@ -409,7 +409,7 @@ EventListenerManager::AddEventListenerInternal(
|
|||
}
|
||||
}
|
||||
|
||||
if (IsApzAwareEvent(aTypeAtom)) {
|
||||
if (IsApzAwareListener(listener)) {
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
|
||||
if (node) {
|
||||
node->SetMayHaveApzAwareListeners();
|
||||
|
@ -545,7 +545,7 @@ EventListenerManager::RemoveEventListenerInternal(
|
|||
aAllEvents)) {
|
||||
++typeCount;
|
||||
if (listener->mListener == aListenerHolder &&
|
||||
listener->mFlags.EqualsIgnoringTrustness(aFlags)) {
|
||||
listener->mFlags.EqualsForRemoval(aFlags)) {
|
||||
RefPtr<EventListenerManager> kungFuDeathGrip(this);
|
||||
mListeners.RemoveElementAt(i);
|
||||
--count;
|
||||
|
@ -1152,9 +1152,11 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
|
|||
}
|
||||
}
|
||||
|
||||
aEvent->mFlags.mInPassiveListener = listener->mFlags.mPassive;
|
||||
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) {
|
||||
aEvent->mFlags.mExceptionHasBeenRisen = true;
|
||||
}
|
||||
aEvent->mFlags.mInPassiveListener = false;
|
||||
|
||||
if (needsEndEventMarker) {
|
||||
timelines->AddMarkerForDocShell(
|
||||
|
@ -1205,9 +1207,12 @@ EventListenerManager::AddEventListener(
|
|||
bool aWantsUntrusted)
|
||||
{
|
||||
EventListenerFlags flags;
|
||||
flags.mCapture =
|
||||
aOptions.IsBoolean() ? aOptions.GetAsBoolean()
|
||||
: aOptions.GetAsAddEventListenerOptions().mCapture;
|
||||
if (aOptions.IsBoolean()) {
|
||||
flags.mCapture = aOptions.GetAsBoolean();
|
||||
} else {
|
||||
flags.mCapture = aOptions.GetAsAddEventListenerOptions().mCapture;
|
||||
flags.mPassive = aOptions.GetAsAddEventListenerOptions().mPassive;
|
||||
}
|
||||
flags.mAllowUntrustedEvents = aWantsUntrusted;
|
||||
return AddEventListenerByType(aListenerHolder, aType, flags);
|
||||
}
|
||||
|
@ -1542,13 +1547,19 @@ EventListenerManager::HasApzAwareListeners()
|
|||
uint32_t count = mListeners.Length();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
Listener* listener = &mListeners.ElementAt(i);
|
||||
if (IsApzAwareEvent(listener->mTypeAtom)) {
|
||||
if (IsApzAwareListener(listener)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
EventListenerManager::IsApzAwareListener(Listener* aListener)
|
||||
{
|
||||
return !aListener->mFlags.mPassive && IsApzAwareEvent(aListener->mTypeAtom);
|
||||
}
|
||||
|
||||
bool
|
||||
EventListenerManager::IsApzAwareEvent(nsIAtom* aEvent)
|
||||
{
|
||||
|
|
|
@ -58,31 +58,32 @@ public:
|
|||
// If mAllowUntrustedEvents is true, the listener is listening to the
|
||||
// untrusted events too.
|
||||
bool mAllowUntrustedEvents : 1;
|
||||
// If mPassive is true, the listener will not be calling preventDefault on the
|
||||
// event. (If it does call preventDefault, we should ignore it).
|
||||
bool mPassive : 1;
|
||||
|
||||
EventListenerFlags() :
|
||||
mListenerIsJSListener(false),
|
||||
mCapture(false), mInSystemGroup(false), mAllowUntrustedEvents(false)
|
||||
mCapture(false), mInSystemGroup(false), mAllowUntrustedEvents(false),
|
||||
mPassive(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool Equals(const EventListenerFlags& aOther) const
|
||||
bool EqualsForAddition(const EventListenerFlags& aOther) const
|
||||
{
|
||||
return (mCapture == aOther.mCapture &&
|
||||
mInSystemGroup == aOther.mInSystemGroup &&
|
||||
mListenerIsJSListener == aOther.mListenerIsJSListener &&
|
||||
mAllowUntrustedEvents == aOther.mAllowUntrustedEvents);
|
||||
// Don't compare mPassive
|
||||
}
|
||||
|
||||
bool EqualsIgnoringTrustness(const EventListenerFlags& aOther) const
|
||||
bool EqualsForRemoval(const EventListenerFlags& aOther) const
|
||||
{
|
||||
return (mCapture == aOther.mCapture &&
|
||||
mInSystemGroup == aOther.mInSystemGroup &&
|
||||
mListenerIsJSListener == aOther.mListenerIsJSListener);
|
||||
}
|
||||
|
||||
bool operator==(const EventListenerFlags& aOther) const
|
||||
{
|
||||
return Equals(aOther);
|
||||
// Don't compare mAllowUntrustedEvents or mPassive
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -444,7 +445,7 @@ public:
|
|||
dom::EventTarget* GetTarget() { return mTarget; }
|
||||
|
||||
bool HasApzAwareListeners();
|
||||
|
||||
bool IsApzAwareListener(Listener* aListener);
|
||||
bool IsApzAwareEvent(nsIAtom* aEvent);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -278,11 +278,13 @@ private:
|
|||
|
||||
#define DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED)
|
||||
|
||||
#define REQUIRED_STATES (NS_EVENT_STATE_REQUIRED | NS_EVENT_STATE_OPTIONAL)
|
||||
|
||||
#define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
|
||||
NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \
|
||||
NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
|
||||
NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | \
|
||||
DISABLED_STATES | \
|
||||
DISABLED_STATES | REQUIRED_STATES | \
|
||||
NS_EVENT_STATE_UNRESOLVED)
|
||||
|
||||
#define INTRINSIC_STATES (~ESM_MANAGED_STATES)
|
||||
|
|
|
@ -311,69 +311,6 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
|||
}
|
||||
break;
|
||||
|
||||
case eMouseDown:
|
||||
{
|
||||
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
|
||||
if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
|
||||
if (mouseEvent->mFlags.mIsTrusted) {
|
||||
EventStateManager* esm =
|
||||
aVisitor.mPresContext->EventStateManager();
|
||||
EventStateManager::SetActiveManager(
|
||||
static_cast<EventStateManager*>(esm), this);
|
||||
}
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
uint32_t flags = nsIFocusManager::FLAG_BYMOUSE |
|
||||
nsIFocusManager::FLAG_NOSCROLL;
|
||||
// If this was a touch-generated event, pass that information:
|
||||
if (mouseEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
|
||||
flags |= nsIFocusManager::FLAG_BYTOUCH;
|
||||
}
|
||||
fm->SetFocus(this, flags);
|
||||
}
|
||||
mouseEvent->mFlags.mMultipleActionsPrevented = true;
|
||||
} else if (mouseEvent->button == WidgetMouseEvent::eMiddleButton ||
|
||||
mouseEvent->button == WidgetMouseEvent::eRightButton) {
|
||||
// cancel all of these events for buttons
|
||||
//XXXsmaug What to do with these events? Why these should be cancelled?
|
||||
if (aVisitor.mDOMEvent) {
|
||||
aVisitor.mDOMEvent->StopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// cancel all of these events for buttons
|
||||
//XXXsmaug What to do with these events? Why these should be cancelled?
|
||||
case eMouseUp:
|
||||
case eMouseDoubleClick:
|
||||
{
|
||||
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
|
||||
if (aVisitor.mDOMEvent &&
|
||||
(mouseEvent->button == WidgetMouseEvent::eMiddleButton ||
|
||||
mouseEvent->button == WidgetMouseEvent::eRightButton)) {
|
||||
aVisitor.mDOMEvent->StopPropagation();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case eMouseOver:
|
||||
{
|
||||
aVisitor.mPresContext->EventStateManager()->
|
||||
SetContentState(this, NS_EVENT_STATE_HOVER);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
break;
|
||||
|
||||
// XXX this doesn't seem to do anything yet
|
||||
case eMouseOut:
|
||||
{
|
||||
aVisitor.mPresContext->EventStateManager()->
|
||||
SetContentState(nullptr, NS_EVENT_STATE_HOVER);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2296,7 +2296,10 @@ HTMLFormElement::AddToRadioGroup(const nsAString& aName,
|
|||
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
|
||||
NS_ASSERTION(element, "radio controls have to be content elements!");
|
||||
|
||||
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
HTMLInputElement* input = HTMLInputElement::FromContent(element);
|
||||
NS_ASSERTION(input, "radio controls have to be input elements!");
|
||||
|
||||
if (input->IsRequired()) {
|
||||
mRequiredRadioButtonCounts.Put(aName,
|
||||
mRequiredRadioButtonCounts.Get(aName)+1);
|
||||
}
|
||||
|
@ -2307,9 +2310,12 @@ HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
|
|||
nsIFormControl* aRadio)
|
||||
{
|
||||
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
|
||||
NS_ASSERTION(element, "radio controls have to be content elements!");
|
||||
NS_ASSERTION(element, "radio controls have to be content elements");
|
||||
|
||||
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
HTMLInputElement* input = HTMLInputElement::FromContent(element);
|
||||
NS_ASSERTION(input, "radio controls have to be input elements!");
|
||||
|
||||
if (input->IsRequired()) {
|
||||
uint32_t requiredNb = mRequiredRadioButtonCounts.Get(aName);
|
||||
NS_ASSERTION(requiredNb >= 1,
|
||||
"At least one radio button has to be required!");
|
||||
|
|
|
@ -1149,6 +1149,13 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||
UpdateDisabledState(aNotify);
|
||||
}
|
||||
|
||||
if (aName == nsGkAtoms::required && DoesRequiredApply()) {
|
||||
// This *has* to be called *before* UpdateValueMissingValidityState
|
||||
// because UpdateValueMissingValidityState depends on our required
|
||||
// state.
|
||||
UpdateRequiredState(!!aValue, aNotify);
|
||||
}
|
||||
|
||||
UpdateValueMissingValidityState();
|
||||
|
||||
// This *has* to be called *after* validity has changed.
|
||||
|
@ -4371,6 +4378,15 @@ HTMLInputElement::HandleTypeChange(uint8_t aNewType)
|
|||
mFocusedValue.Truncate();
|
||||
}
|
||||
|
||||
// Update or clear our required states since we may have changed from a
|
||||
// required input type to a non-required input type or viceversa.
|
||||
if (DoesRequiredApply()) {
|
||||
bool isRequired = HasAttr(kNameSpaceID_None, nsGkAtoms::required);
|
||||
UpdateRequiredState(isRequired, false); // See below for why this is OK.
|
||||
} else {
|
||||
RemoveStatesSilently(REQUIRED_STATES);
|
||||
}
|
||||
|
||||
UpdateHasRange();
|
||||
|
||||
// Do not notify, it will be done after if needed.
|
||||
|
@ -5757,12 +5773,6 @@ HTMLInputElement::IntrinsicState() const
|
|||
state |= nsImageLoadingContent::ImageState();
|
||||
}
|
||||
|
||||
if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
state |= NS_EVENT_STATE_REQUIRED;
|
||||
} else {
|
||||
state |= NS_EVENT_STATE_OPTIONAL;
|
||||
}
|
||||
|
||||
if (IsCandidateForConstraintValidation()) {
|
||||
if (IsValid()) {
|
||||
state |= NS_EVENT_STATE_VALID;
|
||||
|
@ -6369,8 +6379,7 @@ HTMLInputElement::IsValueMissing() const
|
|||
// Should use UpdateValueMissingValidityStateForRadio() for type radio.
|
||||
MOZ_ASSERT(mType != NS_FORM_INPUT_RADIO);
|
||||
|
||||
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) ||
|
||||
!DoesRequiredApply()) {
|
||||
if (!IsRequired() || !DoesRequiredApply()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6635,7 +6644,7 @@ HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
|
|||
// If there is no selection, that might mean the radio is not in a group.
|
||||
// In that case, we can look for the checked state of the radio.
|
||||
bool selected = selection || (!aIgnoreSelf && mChecked);
|
||||
bool required = !aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required);
|
||||
bool required = !aIgnoreSelf && IsRequired();
|
||||
bool valueMissing = false;
|
||||
|
||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
||||
|
@ -6652,7 +6661,7 @@ HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
|
|||
// If the current radio is required and not ignored, we can assume the entire
|
||||
// group is required.
|
||||
if (!required) {
|
||||
required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
|
||||
required = (aIgnoreSelf && IsRequired())
|
||||
? container->GetRequiredRadioCount(name) - 1
|
||||
: container->GetRequiredRadioCount(name);
|
||||
}
|
||||
|
|
|
@ -774,6 +774,22 @@ public:
|
|||
*/
|
||||
static Decimal StringToDecimal(const nsAString& aValue);
|
||||
|
||||
/**
|
||||
* Returns if the required attribute applies for the current type.
|
||||
*/
|
||||
bool DoesRequiredApply() const;
|
||||
|
||||
/**
|
||||
* Returns the current required state of the element. This function differs
|
||||
* from Required() in that this function only returns true for input types
|
||||
* that @required attribute applies and the attribute is set; in contrast,
|
||||
* Required() returns true whenever @required attribute is set.
|
||||
*/
|
||||
bool IsRequired() const
|
||||
{
|
||||
return State().HasState(NS_EVENT_STATE_REQUIRED);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~HTMLInputElement();
|
||||
|
||||
|
@ -964,11 +980,6 @@ protected:
|
|||
*/
|
||||
bool DoesReadOnlyApply() const;
|
||||
|
||||
/**
|
||||
* Returns if the required attribute applies for the current type.
|
||||
*/
|
||||
bool DoesRequiredApply() const;
|
||||
|
||||
/**
|
||||
* Returns if the pattern attribute applies for the current type.
|
||||
*/
|
||||
|
|
|
@ -1070,7 +1070,7 @@ HTMLSelectElement::IsOptionDisabled(int32_t aIndex, bool* aIsDisabled)
|
|||
}
|
||||
|
||||
bool
|
||||
HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption)
|
||||
HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption) const
|
||||
{
|
||||
MOZ_ASSERT(aOption);
|
||||
if (aOption->Disabled()) {
|
||||
|
@ -1301,6 +1301,11 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||
UpdateValueMissingValidityState();
|
||||
UpdateBarredFromConstraintValidation();
|
||||
} else if (aName == nsGkAtoms::required) {
|
||||
// This *has* to be called *before* UpdateValueMissingValidityState
|
||||
// because UpdateValueMissingValidityState depends on our required
|
||||
// state.
|
||||
UpdateRequiredState(!!aValue, aNotify);
|
||||
|
||||
UpdateValueMissingValidityState();
|
||||
} else if (aName == nsGkAtoms::autocomplete) {
|
||||
// Clear the cached @autocomplete attribute state
|
||||
|
@ -1523,12 +1528,6 @@ HTMLSelectElement::IntrinsicState() const
|
|||
}
|
||||
}
|
||||
|
||||
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
state |= NS_EVENT_STATE_REQUIRED;
|
||||
} else {
|
||||
state |= NS_EVENT_STATE_OPTIONAL;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -1771,7 +1770,7 @@ HTMLSelectElement::RebuildOptionsArray(bool aNotify)
|
|||
}
|
||||
|
||||
bool
|
||||
HTMLSelectElement::IsValueMissing()
|
||||
HTMLSelectElement::IsValueMissing() const
|
||||
{
|
||||
if (!Required()) {
|
||||
return false;
|
||||
|
|
|
@ -198,7 +198,7 @@ public:
|
|||
}
|
||||
bool Required() const
|
||||
{
|
||||
return GetBoolAttr(nsGkAtoms::required);
|
||||
return State().HasState(NS_EVENT_STATE_REQUIRED);
|
||||
}
|
||||
void SetRequired(bool aVal, ErrorResult& aRv)
|
||||
{
|
||||
|
@ -332,7 +332,7 @@ public:
|
|||
*/
|
||||
NS_IMETHOD IsOptionDisabled(int32_t aIndex,
|
||||
bool* aIsDisabled);
|
||||
bool IsOptionDisabled(HTMLOptionElement* aOption);
|
||||
bool IsOptionDisabled(HTMLOptionElement* aOption) const;
|
||||
|
||||
/**
|
||||
* Sets multiple options (or just sets startIndex if select is single)
|
||||
|
@ -505,7 +505,7 @@ protected:
|
|||
|
||||
// nsIConstraintValidation
|
||||
void UpdateBarredFromConstraintValidation();
|
||||
bool IsValueMissing();
|
||||
bool IsValueMissing() const;
|
||||
|
||||
/**
|
||||
* Get the index of the first option at, under or following the content in
|
||||
|
|
|
@ -1136,12 +1136,6 @@ HTMLTextAreaElement::IntrinsicState() const
|
|||
{
|
||||
EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
|
||||
|
||||
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
|
||||
state |= NS_EVENT_STATE_REQUIRED;
|
||||
} else {
|
||||
state |= NS_EVENT_STATE_OPTIONAL;
|
||||
}
|
||||
|
||||
if (IsCandidateForConstraintValidation()) {
|
||||
if (IsValid()) {
|
||||
state |= NS_EVENT_STATE_VALID;
|
||||
|
@ -1286,6 +1280,13 @@ HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||
UpdateDisabledState(aNotify);
|
||||
}
|
||||
|
||||
if (aName == nsGkAtoms::required) {
|
||||
// This *has* to be called *before* UpdateValueMissingValidityState
|
||||
// because UpdateValueMissingValidityState depends on our required
|
||||
// state.
|
||||
UpdateRequiredState(!!aValue, aNotify);
|
||||
}
|
||||
|
||||
UpdateValueMissingValidityState();
|
||||
|
||||
// This *has* to be called *after* validity has changed.
|
||||
|
@ -1368,7 +1369,7 @@ HTMLTextAreaElement::IsTooLong()
|
|||
bool
|
||||
HTMLTextAreaElement::IsValueMissing() const
|
||||
{
|
||||
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
|
||||
if (!Required() || !IsMutable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -222,9 +222,9 @@ public:
|
|||
{
|
||||
SetHTMLBoolAttr(nsGkAtoms::readonly, aReadOnly, aError);
|
||||
}
|
||||
bool Required()
|
||||
bool Required() const
|
||||
{
|
||||
return GetBoolAttr(nsGkAtoms::required);
|
||||
return State().HasState(NS_EVENT_STATE_REQUIRED);
|
||||
}
|
||||
|
||||
void SetRangeText(const nsAString& aReplacement, ErrorResult& aRv);
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -2640,6 +2641,24 @@ void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired, bool aNotify)
|
||||
{
|
||||
EventStates requiredStates;
|
||||
if (aIsRequired) {
|
||||
requiredStates |= NS_EVENT_STATE_REQUIRED;
|
||||
} else {
|
||||
requiredStates |= NS_EVENT_STATE_OPTIONAL;
|
||||
}
|
||||
|
||||
EventStates oldRequiredStates = State() & REQUIRED_STATES;
|
||||
EventStates changedStates = requiredStates ^ oldRequiredStates;
|
||||
|
||||
if (!changedStates.IsEmpty()) {
|
||||
ToggleStates(changedStates, aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
|
||||
{
|
||||
|
|
|
@ -1351,6 +1351,11 @@ public:
|
|||
*/
|
||||
void UpdateDisabledState(bool aNotify);
|
||||
|
||||
/**
|
||||
* Update our required/optional flags to match the given aIsRequired boolean.
|
||||
*/
|
||||
void UpdateRequiredState(bool aIsRequired, bool aNotify);
|
||||
|
||||
void FieldSetFirstLegendChanged(bool aNotify) {
|
||||
UpdateFieldSet(aNotify);
|
||||
}
|
||||
|
|
|
@ -9,10 +9,13 @@
|
|||
|
||||
[NoInterfaceObject]
|
||||
interface ChildNode {
|
||||
// Not implemented yet:
|
||||
// void before((Node or DOMString)... nodes);
|
||||
// void after((Node or DOMString)... nodes);
|
||||
// void replace((Node or DOMString)... nodes);
|
||||
[Throws, Unscopable]
|
||||
void before((Node or DOMString)... nodes);
|
||||
[Throws, Unscopable]
|
||||
void after((Node or DOMString)... nodes);
|
||||
[Throws, Unscopable]
|
||||
void replaceWith((Node or DOMString)... nodes);
|
||||
[Unscopable]
|
||||
void remove();
|
||||
};
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ dictionary EventListenerOptions {
|
|||
};
|
||||
|
||||
dictionary AddEventListenerOptions : EventListenerOptions {
|
||||
// boolean passive = false;
|
||||
boolean passive = false;
|
||||
// XXX: this is bug 1287706 and follow-on 1367372
|
||||
// boolean once = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ interface ParentNode {
|
|||
[Pure]
|
||||
readonly attribute unsigned long childElementCount;
|
||||
|
||||
// Not implemented yet
|
||||
// void prepend((Node or DOMString)... nodes);
|
||||
// void append((Node or DOMString)... nodes);
|
||||
[Throws, Unscopable]
|
||||
void prepend((Node or DOMString)... nodes);
|
||||
[Throws, Unscopable]
|
||||
void append((Node or DOMString)... nodes);
|
||||
};
|
||||
|
|
|
@ -3473,15 +3473,6 @@ nsEditor::IsEditable(nsINode* aNode)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsEditor::IsMozEditorBogusNode(nsINode* element)
|
||||
{
|
||||
return element && element->IsElement() &&
|
||||
element->AsElement()->AttrValueIs(kNameSpaceID_None,
|
||||
kMOZEditorBogusNodeAttrAtom, kMOZEditorBogusNodeValue,
|
||||
eCaseMatters);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsEditor::CountEditableChildren(nsINode* aNode)
|
||||
{
|
||||
|
@ -3625,12 +3616,6 @@ nsEditor::IsTextNode(nsIDOMNode *aNode)
|
|||
return (nodeType == nsIDOMNode::TEXT_NODE);
|
||||
}
|
||||
|
||||
bool
|
||||
nsEditor::IsTextNode(nsINode *aNode)
|
||||
{
|
||||
return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// GetChildAt: returns the node at this position index in the parent
|
||||
//
|
||||
|
@ -4844,20 +4829,6 @@ nsEditor::FinalizeSelection()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
dom::Element *
|
||||
nsEditor::GetRoot()
|
||||
{
|
||||
if (!mRootElement)
|
||||
{
|
||||
nsCOMPtr<nsIDOMElement> root;
|
||||
|
||||
// Let GetRootElement() do the work
|
||||
GetRootElement(getter_AddRefs(root));
|
||||
}
|
||||
|
||||
return mRootElement;
|
||||
}
|
||||
|
||||
dom::Element*
|
||||
nsEditor::GetEditorRoot()
|
||||
{
|
||||
|
|
|
@ -569,7 +569,13 @@ public:
|
|||
virtual bool IsEditable(nsINode* aNode);
|
||||
|
||||
/** returns true if aNode is a MozEditorBogus node */
|
||||
bool IsMozEditorBogusNode(nsINode* aNode);
|
||||
bool IsMozEditorBogusNode(nsINode* aNode)
|
||||
{
|
||||
return aNode && aNode->IsElement() &&
|
||||
aNode->AsElement()->AttrValueIs(kNameSpaceID_None,
|
||||
kMOZEditorBogusNodeAttrAtom, kMOZEditorBogusNodeValue,
|
||||
eCaseMatters);
|
||||
}
|
||||
|
||||
/** counts number of editable child nodes */
|
||||
uint32_t CountEditableChildren(nsINode* aNode);
|
||||
|
@ -598,7 +604,10 @@ public:
|
|||
virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2);
|
||||
|
||||
static bool IsTextNode(nsIDOMNode *aNode);
|
||||
static bool IsTextNode(nsINode *aNode);
|
||||
static bool IsTextNode(nsINode* aNode)
|
||||
{
|
||||
return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
|
||||
}
|
||||
|
||||
static nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode *aParent, int32_t aOffset);
|
||||
static nsCOMPtr<nsIDOMNode> GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset);
|
||||
|
@ -664,7 +673,16 @@ public:
|
|||
virtual already_AddRefed<mozilla::dom::EventTarget> GetDOMEventTarget() = 0;
|
||||
|
||||
// Fast non-refcounting editor root element accessor
|
||||
mozilla::dom::Element *GetRoot();
|
||||
mozilla::dom::Element *GetRoot()
|
||||
{
|
||||
if (!mRootElement) {
|
||||
// Let GetRootElement() do the work
|
||||
nsCOMPtr<nsIDOMElement> root;
|
||||
GetRootElement(getter_AddRefs(root));
|
||||
}
|
||||
|
||||
return mRootElement;
|
||||
}
|
||||
|
||||
// Likewise, but gets the editor's root instead, which is different for HTML
|
||||
// editors
|
||||
|
|
|
@ -1176,11 +1176,12 @@ nsTextEditRules::CreateBogusNodeIfNeeded(Selection* aSelection)
|
|||
// Now we've got the body element. Iterate over the body element's children,
|
||||
// looking for editable content. If no editable content is found, insert the
|
||||
// bogus node.
|
||||
for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
|
||||
bool bodyEditable = mEditor->IsEditable(body);
|
||||
for (nsIContent* bodyChild = body->GetFirstChild();
|
||||
bodyChild;
|
||||
bodyChild = bodyChild->GetNextSibling()) {
|
||||
if (mEditor->IsMozEditorBogusNode(bodyChild) ||
|
||||
!mEditor->IsEditable(body) || // XXX hoist out of the loop?
|
||||
!bodyEditable ||
|
||||
mEditor->IsEditable(bodyChild) || mEditor->IsBlockNode(bodyChild)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
#include "irregexp/RegExpEngine.h"
|
||||
|
||||
#include "irregexp/NativeRegExpMacroAssembler.h"
|
||||
|
|
37
js/src/jit-test/tests/debug/Environment-unscopables.js
Normal file
37
js/src/jit-test/tests/debug/Environment-unscopables.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
// An Environment for a `with` statement does not observe bindings ruled out by @@unscopables.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
let g = newGlobal();
|
||||
g.eval(`
|
||||
let x = 'global';
|
||||
function f() {
|
||||
let obj = {
|
||||
x: 'obj',
|
||||
y: 'obj',
|
||||
[Symbol.unscopables]: {x: 1},
|
||||
};
|
||||
with (obj)
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
let dbg = Debugger(g);
|
||||
let hits = 0;
|
||||
dbg.onDebuggerStatement = function (frame) {
|
||||
let env = frame.environment;
|
||||
|
||||
assertEq(env.find("x") !== env, true);
|
||||
assertEq(env.names().indexOf("x"), -1);
|
||||
assertEq(env.getVariable("x"), undefined);
|
||||
assertThrowsInstanceOf(() => env.setVariable("x", 7), TypeError);
|
||||
|
||||
assertEq(env.find("y") === env, true);
|
||||
assertEq(env.getVariable("y"), "obj");
|
||||
env.setVariable("y", 8);
|
||||
|
||||
assertEq(frame.eval("x").return, "global");
|
||||
assertEq(frame.eval("y").return, 8);
|
||||
hits++;
|
||||
};
|
||||
g.f();
|
||||
assertEq(hits, 1);
|
|
@ -1,6 +0,0 @@
|
|||
if (helperThreadCount() === 0)
|
||||
quit();
|
||||
offThreadCompileScript("");
|
||||
startgc(0);
|
||||
runOffThreadScript();
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
if (!("setGCCallback" in this &&
|
||||
"schedulegc" in this &&
|
||||
"gcslice" in this &&
|
||||
"newGlobal" in this &&
|
||||
"helperThreadCount" in this))
|
||||
{
|
||||
quit();
|
||||
}
|
||||
|
||||
if (helperThreadCount() == 0)
|
||||
quit();
|
||||
|
||||
setGCCallback({
|
||||
action: "majorGC",
|
||||
});
|
||||
schedulegc(this)
|
||||
gcslice(3)
|
||||
var lfGlobal = newGlobal();
|
||||
lfGlobal.offThreadCompileScript("");
|
||||
lfGlobal.runOffThreadScript();
|
||||
|
|
@ -4556,17 +4556,25 @@ JS_PUBLIC_API(JSString*)
|
|||
GetSymbolDescription(HandleSymbol symbol);
|
||||
|
||||
/* Well-known symbols. */
|
||||
#define JS_FOR_EACH_WELL_KNOWN_SYMBOL(macro) \
|
||||
macro(iterator) \
|
||||
macro(match) \
|
||||
macro(species) \
|
||||
macro(toPrimitive) \
|
||||
macro(unscopables)
|
||||
|
||||
enum class SymbolCode : uint32_t {
|
||||
iterator, // well-known symbols
|
||||
match,
|
||||
species,
|
||||
toPrimitive,
|
||||
// There is one SymbolCode for each well-known symbol.
|
||||
#define JS_DEFINE_SYMBOL_ENUM(name) name,
|
||||
JS_FOR_EACH_WELL_KNOWN_SYMBOL(JS_DEFINE_SYMBOL_ENUM) // SymbolCode::iterator, etc.
|
||||
#undef JS_DEFINE_SYMBOL_ENUM
|
||||
Limit,
|
||||
InSymbolRegistry = 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor()
|
||||
UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol()
|
||||
};
|
||||
|
||||
/* For use in loops that iterate over the well-known symbols. */
|
||||
const size_t WellKnownSymbolLimit = 4;
|
||||
const size_t WellKnownSymbolLimit = size_t(SymbolCode::Limit);
|
||||
|
||||
/**
|
||||
* Return the SymbolCode telling what sort of symbol `symbol` is.
|
||||
|
|
|
@ -3278,6 +3278,32 @@ CreateArrayPrototype(JSContext* cx, JSProtoKey key)
|
|||
return arrayProto;
|
||||
}
|
||||
|
||||
static bool
|
||||
array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
||||
{
|
||||
// Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
|
||||
RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
|
||||
if (!unscopables)
|
||||
return false;
|
||||
|
||||
RootedValue value(cx, BooleanValue(true));
|
||||
if (!DefineProperty(cx, unscopables, cx->names().copyWithin, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().entries, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().fill, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().find, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().findIndex, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().values, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
|
||||
value.setObject(*unscopables);
|
||||
return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
|
||||
}
|
||||
|
||||
const Class ArrayObject::class_ = {
|
||||
"Array",
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_CALLBACK,
|
||||
|
@ -3298,7 +3324,9 @@ const Class ArrayObject::class_ = {
|
|||
CreateArrayPrototype,
|
||||
array_static_methods,
|
||||
nullptr,
|
||||
array_methods
|
||||
array_methods,
|
||||
nullptr,
|
||||
array_proto_finish
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -38,9 +38,6 @@ IdIsIndex(jsid id, uint32_t* indexp)
|
|||
return js::StringIsArrayIndex(atom, indexp);
|
||||
}
|
||||
|
||||
extern JSObject*
|
||||
InitArrayClass(JSContext* cx, js::HandleObject obj);
|
||||
|
||||
// The methods below only create dense boxed arrays.
|
||||
|
||||
/* Create a dense array with no capacity allocated, length set to 0. */
|
||||
|
|
|
@ -149,6 +149,8 @@ class ExclusiveContext : public ContextFriendFields,
|
|||
return isJSContext();
|
||||
}
|
||||
|
||||
JSRuntime* ecRuntime() const { return runtime_; } // TenFourFox issue 391
|
||||
|
||||
bool runtimeMatches(JSRuntime* rt) const {
|
||||
return runtime_ == rt;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,130 @@
|
|||
#include "vm/ProxyObject.h"
|
||||
#include "vm/Symbol.h"
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
JSContext::runningWithTrustedPrincipals() const
|
||||
{
|
||||
return !compartment() || compartment()->principals() == runtime()->trustedPrincipals();
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t
|
||||
GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0)
|
||||
{
|
||||
PerThreadDataFriendFields* mainThread =
|
||||
PerThreadDataFriendFields::getMainThread(GetRuntime(cx));
|
||||
uintptr_t limit = mainThread->nativeStackLimit[kind];
|
||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
limit += extraAllowance;
|
||||
#else
|
||||
limit -= extraAllowance;
|
||||
#endif
|
||||
return limit;
|
||||
}
|
||||
|
||||
// Needed for issue 391 -- see below
|
||||
MOZ_ALWAYS_INLINE uintptr_t
|
||||
GetNativeStackLimit(ExclusiveContext* cx, StackKind kind, int extraAllowance = 0)
|
||||
{
|
||||
PerThreadDataFriendFields* mainThread =
|
||||
PerThreadDataFriendFields::getMainThread(cx->ecRuntime());
|
||||
uintptr_t limit = mainThread->nativeStackLimit[kind];
|
||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
limit += extraAllowance;
|
||||
#else
|
||||
limit -= extraAllowance;
|
||||
#endif
|
||||
return limit;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t
|
||||
GetNativeStackLimit(JSContext* cx, int extraAllowance = 0)
|
||||
{
|
||||
StackKind kind = cx->runningWithTrustedPrincipals() ? StackForTrustedScript
|
||||
: StackForUntrustedScript;
|
||||
return GetNativeStackLimit(cx, kind, extraAllowance);
|
||||
}
|
||||
|
||||
/*
|
||||
* These macros report a stack overflow and run |onerror| if we are close to
|
||||
* using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a
|
||||
* little extra space so that we can ensure that crucial code is able to run.
|
||||
* JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check,
|
||||
* including a safety buffer (as in, it uses the untrusted limit and subtracts
|
||||
* a little more from it).
|
||||
*/
|
||||
|
||||
// Implement a fast path a la bug 1342439, but without all that churn.
|
||||
// Leave the old limit versions here just in case.
|
||||
// TenFourFox issue 391
|
||||
|
||||
#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
int stackDummy_; \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \
|
||||
js::ReportOverRecursed(cx); \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION(cx, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
int stackDummy_; \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx, js::StackForUntrustedScript), &stackDummy_))) { \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_))) {\
|
||||
js::ReportOverRecursed(cx); \
|
||||
onerror; \
|
||||
} \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
int stackDummy_; \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
int stackDummy_; \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx, js::StackForUntrustedScript), &stackDummy_))) { \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_))) {\
|
||||
onerror; \
|
||||
} \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \
|
||||
js::ReportOverRecursed(cx); \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror)
|
||||
|
||||
#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT(cx, \
|
||||
js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \
|
||||
onerror)
|
||||
|
||||
#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \
|
||||
js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \
|
||||
onerror)
|
||||
|
||||
class CompartmentChecker
|
||||
{
|
||||
JSCompartment* compartment;
|
||||
|
@ -382,12 +504,6 @@ JSContext::setPendingException(js::Value v)
|
|||
MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSContext::runningWithTrustedPrincipals() const
|
||||
{
|
||||
return !compartment() || compartment()->principals() == runtime()->trustedPrincipals();
|
||||
}
|
||||
|
||||
inline void
|
||||
js::ExclusiveContext::enterCompartment(JSCompartment* c)
|
||||
{
|
||||
|
|
|
@ -385,9 +385,24 @@ js::IsObjectInContextCompartment(JSObject* obj, const JSContext* cx)
|
|||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::RunningWithTrustedPrincipals(JSContext* cx)
|
||||
js::CheckRecursion(JSContext* cx)
|
||||
{
|
||||
return cx->runningWithTrustedPrincipals();
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::CheckRecursionConservative(JSContext* cx)
|
||||
{
|
||||
JS_CHECK_RECURSION_CONSERVATIVE(cx, return false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::CheckRecursionConservativeDontReport(JSContext* cx)
|
||||
{
|
||||
JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, return false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction*)
|
||||
|
|
|
@ -967,89 +967,13 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx);
|
|||
#define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
RunningWithTrustedPrincipals(JSContext* cx);
|
||||
CheckRecursion(JSContext* cx);
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t
|
||||
GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0)
|
||||
{
|
||||
PerThreadDataFriendFields* mainThread =
|
||||
PerThreadDataFriendFields::getMainThread(GetRuntime(cx));
|
||||
uintptr_t limit = mainThread->nativeStackLimit[kind];
|
||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||
limit += extraAllowance;
|
||||
#else
|
||||
limit -= extraAllowance;
|
||||
#endif
|
||||
return limit;
|
||||
}
|
||||
JS_FRIEND_API(bool)
|
||||
CheckRecursionConservative(JSContext* cx);
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t
|
||||
GetNativeStackLimit(JSContext* cx, int extraAllowance = 0)
|
||||
{
|
||||
StackKind kind = RunningWithTrustedPrincipals(cx) ? StackForTrustedScript
|
||||
: StackForUntrustedScript;
|
||||
return GetNativeStackLimit(cx, kind, extraAllowance);
|
||||
}
|
||||
|
||||
/*
|
||||
* These macros report a stack overflow and run |onerror| if we are close to
|
||||
* using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a
|
||||
* little extra space so that we can ensure that crucial code is able to run.
|
||||
* JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check,
|
||||
* including a safety buffer (as in, it uses the untrusted limit and subtracts
|
||||
* a little more from it).
|
||||
*/
|
||||
|
||||
#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
int stackDummy_; \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \
|
||||
js::ReportOverRecursed(cx); \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx), onerror)
|
||||
|
||||
#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
int stackDummy_; \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, js::GetNativeStackLimit(cx), onerror)
|
||||
|
||||
#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \
|
||||
js::ReportOverRecursed(cx); \
|
||||
onerror; \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror)
|
||||
|
||||
#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT(cx, \
|
||||
js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \
|
||||
onerror)
|
||||
|
||||
#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \
|
||||
JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \
|
||||
js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \
|
||||
onerror)
|
||||
JS_FRIEND_API(bool)
|
||||
CheckRecursionConservativeDontReport(JSContext* cx);
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
StartPCCountProfiling(JSContext* cx);
|
||||
|
|
|
@ -6845,11 +6845,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
|
|||
|
||||
JSRuntime* rt = source->runtimeFromMainThread();
|
||||
|
||||
MOZ_ASSERT(!source->zone()->wasGCStarted());
|
||||
MOZ_ASSERT(!target->zone()->wasGCStarted());
|
||||
JS::AutoAssertOnGC nogc(rt);
|
||||
|
||||
AutoTraceSession session(rt);
|
||||
AutoPrepareForTracing prepare(rt, SkipAtoms);
|
||||
|
||||
// Cleanup tables and other state in the source compartment that will be
|
||||
// meaningless after merging into the target compartment.
|
||||
|
|
|
@ -606,6 +606,64 @@ js::SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t l
|
|||
return NewDependentString(cx, str, begin, len);
|
||||
}
|
||||
|
||||
// Adapted from bug 1383647
|
||||
static inline bool
|
||||
FastLatin1LowerCase(Latin1Char ch)
|
||||
{
|
||||
if (MOZ_LIKELY(ch < 128))
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
// U+00C0 to U+00DE, except U+00D7, have a lowercase form.
|
||||
bool canLower = ((ch & ~0x1F) == /* LATIN_CAPITAL_LETTER_A_WITH_GRAVE */ 0xc0) &&
|
||||
((ch & /* MULTIPLICATION_SIGN */ 0xd7) != 0xd7);
|
||||
MOZ_ASSERT(canLower == CanLowerCase(char16_t(ch)));
|
||||
return canLower;
|
||||
}
|
||||
|
||||
static JSString*
|
||||
ToLowerCaseLatin1(JSContext* cx, JSLinearString* str)
|
||||
{
|
||||
// Unlike toUpperCase, toLowerCase has the nice invariant that if the input
|
||||
// is a Latin1 string, the output is also a Latin1 string.
|
||||
UniquePtr<Latin1Char[], JS::FreePolicy> newChars;
|
||||
size_t length = str->length();
|
||||
{
|
||||
AutoCheckCannotGC nogc;
|
||||
const Latin1Char* chars = str->chars<Latin1Char>(nogc);
|
||||
|
||||
// Look for the first upper case character.
|
||||
size_t i = 0;
|
||||
for (; i < length; i++) {
|
||||
if (FastLatin1LowerCase(chars[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
// If all characters are lower case, return the input string.
|
||||
if (i == length)
|
||||
return str;
|
||||
|
||||
newChars = cx->make_pod_array<Latin1Char>(length + 1);
|
||||
if (MOZ_UNLIKELY(!newChars))
|
||||
return nullptr;
|
||||
|
||||
PodCopy(newChars.get(), chars, i);
|
||||
|
||||
for (; i < length; i++) {
|
||||
char16_t c = unicode::ToLowerCase(chars[i]);
|
||||
MOZ_ASSERT_IF((IsSame<Latin1Char, Latin1Char>::value), c <= JSString::MAX_LATIN1_CHAR);
|
||||
newChars[i] = c;
|
||||
}
|
||||
|
||||
newChars[length] = 0;
|
||||
}
|
||||
|
||||
JSString* res = NewStringDontDeflate<CanGC>(cx, newChars.get(), length);
|
||||
if (MOZ_UNLIKELY(!res))
|
||||
return nullptr;
|
||||
|
||||
newChars.release();
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static JSString*
|
||||
ToLowerCase(JSContext* cx, JSLinearString* str)
|
||||
|
@ -631,7 +689,7 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
return str;
|
||||
|
||||
newChars = cx->make_pod_array<CharT>(length + 1);
|
||||
if (!newChars)
|
||||
if (MOZ_UNLIKELY(!newChars))
|
||||
return nullptr;
|
||||
|
||||
PodCopy(newChars.get(), chars, i);
|
||||
|
@ -646,7 +704,7 @@ ToLowerCase(JSContext* cx, JSLinearString* str)
|
|||
}
|
||||
|
||||
JSString* res = NewStringDontDeflate<CanGC>(cx, newChars.get(), length);
|
||||
if (!res)
|
||||
if (MOZ_UNLIKELY(!res))
|
||||
return nullptr;
|
||||
|
||||
newChars.release();
|
||||
|
@ -657,18 +715,18 @@ static inline bool
|
|||
ToLowerCaseHelper(JSContext* cx, CallReceiver call)
|
||||
{
|
||||
RootedString str(cx, ThisToStringForStringProto(cx, call));
|
||||
if (!str)
|
||||
if (MOZ_UNLIKELY(!str))
|
||||
return false;
|
||||
|
||||
JSLinearString* linear = str->ensureLinear(cx);
|
||||
if (!linear)
|
||||
if (MOZ_UNLIKELY(!linear))
|
||||
return false;
|
||||
|
||||
if (linear->hasLatin1Chars())
|
||||
str = ToLowerCase<Latin1Char>(cx, linear);
|
||||
str = ToLowerCaseLatin1(cx, linear);
|
||||
else
|
||||
str = ToLowerCase<char16_t>(cx, linear);
|
||||
if (!str)
|
||||
if (MOZ_UNLIKELY(!str))
|
||||
return false;
|
||||
|
||||
call.rval().setString(str);
|
||||
|
|
51
js/src/tests/ecma_6/Array/unscopables.js
Normal file
51
js/src/tests/ecma_6/Array/unscopables.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
let Array_unscopables = Array.prototype[Symbol.unscopables];
|
||||
|
||||
let desc = Reflect.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables);
|
||||
assertDeepEq(desc, {
|
||||
value: Array_unscopables,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
assertEq(Reflect.getPrototypeOf(Array_unscopables), null);
|
||||
|
||||
let desc2 = Object.getOwnPropertyDescriptor(Array_unscopables, "values");
|
||||
assertDeepEq(desc2, {
|
||||
value: true,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
let keys = Reflect.ownKeys(Array_unscopables);
|
||||
print(uneval(keys));
|
||||
assertDeepEq(keys, [
|
||||
"copyWithin",
|
||||
"entries",
|
||||
"fill",
|
||||
"find",
|
||||
"findIndex",
|
||||
"includes",
|
||||
"keys",
|
||||
"values"
|
||||
]);
|
||||
|
||||
for (let key of keys)
|
||||
assertEq(Array_unscopables[key], true);
|
||||
|
||||
// Test that it actually works
|
||||
assertThrowsInstanceOf(() => {
|
||||
with ([]) {
|
||||
return entries;
|
||||
}
|
||||
}, ReferenceError);
|
||||
|
||||
{
|
||||
let fill = 33;
|
||||
with (Array.prototype) {
|
||||
assertEq(fill, 33);
|
||||
}
|
||||
}
|
||||
|
||||
reportCompare(0, 0);
|
22
js/src/tests/ecma_6/LexicalEnvironment/unscopables-basics.js
Normal file
22
js/src/tests/ecma_6/LexicalEnvironment/unscopables-basics.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Basics of @@unscopables support.
|
||||
|
||||
// In with(obj), if obj[@@unscopables][id] is truthy, then the identifier id
|
||||
// is not present as a binding in the with-block's scope.
|
||||
var x = "global";
|
||||
with ({x: "with", [Symbol.unscopables]: {x: true}})
|
||||
assertEq(x, "global");
|
||||
|
||||
// But if obj[@@unscopables][id] is false or not present, there is a binding.
|
||||
with ({y: "with", z: "with", [Symbol.unscopables]: {y: false}}) {
|
||||
assertEq(y, "with");
|
||||
assertEq(z, "with");
|
||||
}
|
||||
|
||||
// ToBoolean(obj[@@unscopables][id]) determines whether there's a binding.
|
||||
let someValues = [0, -0, NaN, "", undefined, null, "x", {}, []];
|
||||
for (let v of someValues) {
|
||||
with ({x: "with", [Symbol.unscopables]: {x: v}})
|
||||
assertEq(x, v ? "global" : "with");
|
||||
}
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,23 @@
|
|||
// @@unscopables continues to work after exiting the relevant `with` block,
|
||||
// if the environment is captured by a closure.
|
||||
|
||||
let env = {
|
||||
x: 9000,
|
||||
[Symbol.unscopables]: {x: true}
|
||||
};
|
||||
|
||||
function make_adder(x) {
|
||||
with (env)
|
||||
return function (y) { return x + y; };
|
||||
}
|
||||
assertEq(make_adder(3)(10), 13);
|
||||
|
||||
// Same test, but with a bunch of different parts for bad luck
|
||||
let x = 500;
|
||||
function make_adder_with_eval() {
|
||||
with (env)
|
||||
return eval('y => eval("x + y")');
|
||||
}
|
||||
assertEq(make_adder_with_eval()(10), 510);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,8 @@
|
|||
// @@unscopables prevents a property from having any effect on assigning to a
|
||||
// const binding (which is an error).
|
||||
|
||||
const x = 1;
|
||||
with ({x: 1, [Symbol.unscopables]: {x: true}})
|
||||
assertThrowsInstanceOf(() => {x = 2;}, TypeError);
|
||||
|
||||
reportCompare(0, 0);
|
27
js/src/tests/ecma_6/LexicalEnvironment/unscopables-delete.js
Normal file
27
js/src/tests/ecma_6/LexicalEnvironment/unscopables-delete.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
// If obj[@@unscopables][id], then `delete id` works across `with (obj)` scope.
|
||||
|
||||
this.niche = 7;
|
||||
let obj = { niche: 8, [Symbol.unscopables]: { niche: true } };
|
||||
with (obj) {
|
||||
delete niche;
|
||||
}
|
||||
|
||||
assertEq(obj.niche, 8);
|
||||
assertEq("niche" in this, false);
|
||||
|
||||
// Same thing, but delete a variable introduced by sloppy direct eval.
|
||||
this.niche = 9;
|
||||
function f() {
|
||||
eval("var niche = 10;");
|
||||
with (obj) {
|
||||
assertEq(niche, 10);
|
||||
delete niche;
|
||||
}
|
||||
assertEq(niche, 9);
|
||||
}
|
||||
|
||||
// Of course none of this affects a qualified delete.
|
||||
assertEq(delete this.niche, true);
|
||||
assertEq("niche" in this, false);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,41 @@
|
|||
// @@unscopables checks can call getters.
|
||||
|
||||
// The @@unscopables property itself can be a getter.
|
||||
let hit1 = 0;
|
||||
let x = "global x";
|
||||
let env1 = {
|
||||
x: "env1.x",
|
||||
get [Symbol.unscopables]() {
|
||||
hit1++;
|
||||
return {x: true};
|
||||
}
|
||||
};
|
||||
with (env1)
|
||||
assertEq(x, "global x");
|
||||
assertEq(hit1, 1);
|
||||
|
||||
// It can throw; the exception is propagated out.
|
||||
function Fit() {}
|
||||
with ({x: 0, get [Symbol.unscopables]() { throw new Fit; }})
|
||||
assertThrowsInstanceOf(() => x, Fit);
|
||||
|
||||
// Individual properties on the @@unscopables object can have getters.
|
||||
let hit2 = 0;
|
||||
let env2 = {
|
||||
x: "env2.x",
|
||||
[Symbol.unscopables]: {
|
||||
get x() {
|
||||
hit2++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
with (env2)
|
||||
assertEq(x, "global x");
|
||||
assertEq(hit2, 1);
|
||||
|
||||
// And they can throw.
|
||||
with ({x: 0, [Symbol.unscopables]: {get x() { throw new Fit; }}})
|
||||
assertThrowsInstanceOf(() => x, Fit);
|
||||
|
||||
reportCompare(0, 0);
|
18
js/src/tests/ecma_6/LexicalEnvironment/unscopables-global.js
Normal file
18
js/src/tests/ecma_6/LexicalEnvironment/unscopables-global.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
// @@unscopables does not affect the global environment.
|
||||
|
||||
this.x = "global property x";
|
||||
let y = "global lexical y";
|
||||
this[Symbol.unscopables] = {x: true, y: true};
|
||||
assertEq(x, "global property x");
|
||||
assertEq(y, "global lexical y");
|
||||
assertEq(eval("x"), "global property x");
|
||||
assertEq(eval("y"), "global lexical y");
|
||||
|
||||
// But it does affect `with` statements targeting the global object.
|
||||
{
|
||||
let x = "local x";
|
||||
with (this)
|
||||
assertEq(x, "local x");
|
||||
}
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,22 @@
|
|||
// In these cases, @@unscopables should not be consulted.
|
||||
|
||||
// Because obj has no properties `assertEq` or `x`,
|
||||
// obj[@@unscopables] is not checked here:
|
||||
var obj = {
|
||||
get [Symbol.unscopables]() {
|
||||
throw "tried to read @@unscopables";
|
||||
}
|
||||
};
|
||||
var x = 3;
|
||||
with (obj)
|
||||
assertEq(x, 3);
|
||||
|
||||
// If @@unscopables is present but not an object, it is ignored:
|
||||
for (let nonObject of [undefined, null, "nothing", Symbol.for("moon")]) {
|
||||
let y = 4;
|
||||
let obj2 = {[Symbol.unscopables]: nonObject, y: 5};
|
||||
with (obj2)
|
||||
assertEq(y, 5);
|
||||
}
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,7 @@
|
|||
// Trying to access a binding that doesn't exist due to @@unscopables
|
||||
// is a ReferenceError.
|
||||
|
||||
with ({x: 1, [Symbol.unscopables]: {x: true}})
|
||||
assertThrowsInstanceOf(() => x, ReferenceError);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,18 @@
|
|||
// When env[@@unscopables].x changes, bindings can appear even if env is inextensible.
|
||||
|
||||
let x = "global";
|
||||
let unscopables = {x: true};
|
||||
let env = Object.create(null);
|
||||
env[Symbol.unscopables] = unscopables;
|
||||
env.x = "object";
|
||||
Object.freeze(env);
|
||||
|
||||
for (let i = 0; i < 1004; i++) {
|
||||
if (i === 1000)
|
||||
unscopables.x = false;
|
||||
with (env) {
|
||||
assertEq(x, i < 1000 ? "global" : "object");
|
||||
}
|
||||
}
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,44 @@
|
|||
// When obj[@@unscopables].x changes, bindings appear and disappear accordingly.
|
||||
|
||||
let x = "global";
|
||||
function getX() { return x; }
|
||||
|
||||
let unscopables = {x: true};
|
||||
let obj = {x: "obj", [Symbol.unscopables]: unscopables};
|
||||
|
||||
with (obj) {
|
||||
assertEq(x, "global");
|
||||
x = "global-1";
|
||||
assertEq(x, "global-1");
|
||||
assertEq(obj.x, "obj");
|
||||
|
||||
unscopables.x = false; // suddenly x appears in the with-environment
|
||||
|
||||
assertEq(x, "obj");
|
||||
x = "obj-1";
|
||||
assertEq(getX(), "global-1"); // unchanged
|
||||
assertEq(obj.x, "obj-1");
|
||||
|
||||
unscopables.x = true; // *poof*
|
||||
|
||||
assertEq(x, "global-1");
|
||||
x = "global-2";
|
||||
assertEq(getX(), "global-2");
|
||||
assertEq(obj.x, "obj-1"); // unchanged
|
||||
|
||||
// The determination of which binding is assigned happens when the LHS of
|
||||
// assignment is evaluated, before the RHS. This is observable if we make
|
||||
// the binding appear or disappear during evaluation of the RHS, before
|
||||
// assigning.
|
||||
x = (unscopables.x = false, "global-3");
|
||||
assertEq(getX(), "global-3");
|
||||
assertEq(obj.x, "obj-1");
|
||||
|
||||
x = (unscopables.x = true, "obj-2");
|
||||
assertEq(getX(), "global-3");
|
||||
assertEq(obj.x, "obj-2");
|
||||
}
|
||||
|
||||
assertEq(x, "global-3");
|
||||
|
||||
reportCompare(0, 0);
|
39
js/src/tests/ecma_6/LexicalEnvironment/unscopables-proto.js
Normal file
39
js/src/tests/ecma_6/LexicalEnvironment/unscopables-proto.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
// @@unscopables treats properties found on prototype chains the same as other
|
||||
// properties.
|
||||
|
||||
const x = "global x";
|
||||
const y = "global y";
|
||||
|
||||
// obj[@@unscopables].x works when obj.x is inherited via the prototype chain.
|
||||
let proto = {x: "object x", y: "object y"};
|
||||
let env = Object.create(proto);
|
||||
env[Symbol.unscopables] = {x: true, y: false};
|
||||
with (env) {
|
||||
assertEq(x, "global x");
|
||||
assertEq(delete x, false);
|
||||
assertEq(y, "object y");
|
||||
}
|
||||
assertEq(env.x, "object x");
|
||||
|
||||
// @@unscopables works if is inherited via the prototype chain.
|
||||
env = {
|
||||
x: "object",
|
||||
[Symbol.unscopables]: {x: true, y: true}
|
||||
};
|
||||
for (let i = 0; i < 50; i++)
|
||||
env = Object.create(env);
|
||||
env.y = 1;
|
||||
with (env) {
|
||||
assertEq(x, "global x");
|
||||
assertEq(y, "global y");
|
||||
}
|
||||
|
||||
// @@unscopables works if the obj[@@unscopables][id] property is inherited.
|
||||
env = {
|
||||
x: "object",
|
||||
[Symbol.unscopables]: Object.create({x: true})
|
||||
};
|
||||
with (env)
|
||||
assertEq(x, "global x");
|
||||
|
||||
reportCompare(0, 0);
|
46
js/src/tests/ecma_6/LexicalEnvironment/unscopables-proxy.js
Normal file
46
js/src/tests/ecma_6/LexicalEnvironment/unscopables-proxy.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Object operations are performed in the right order, as observed by proxies.
|
||||
|
||||
let log = [];
|
||||
function LoggingProxyHandlerWrapper(name, handler={}) {
|
||||
return new Proxy(handler, {
|
||||
get(t, id) {
|
||||
let method = handler[id];
|
||||
return function (...args) {
|
||||
log.push([name + "." + id, ...args.filter(v => typeof v !== "object")]);
|
||||
if (method === undefined)
|
||||
return Reflect[id].apply(null, args);
|
||||
return method.apply(this, args);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function LoggingProxy(name, target) {
|
||||
return new Proxy(target, new LoggingProxyHandlerWrapper(name));
|
||||
}
|
||||
|
||||
let proto = {x: 44};
|
||||
let proto_proxy = new LoggingProxy("proto", proto);
|
||||
let unscopables = {x: true};
|
||||
let unscopables_proxy = new LoggingProxy("unscopables", {x: true});
|
||||
let env = Object.create(proto_proxy, {
|
||||
[Symbol.unscopables]: { value: unscopables_proxy }
|
||||
});
|
||||
let env_proxy = new LoggingProxy("env", env);
|
||||
|
||||
let x = 11;
|
||||
function f() {
|
||||
with (env_proxy)
|
||||
return x;
|
||||
}
|
||||
|
||||
assertEq(f(), 11);
|
||||
|
||||
assertDeepEq(log, [
|
||||
["env.has", "x"],
|
||||
["proto.has", "x"],
|
||||
["env.get", Symbol.unscopables],
|
||||
["unscopables.get", "x"]
|
||||
]);
|
||||
|
||||
reportCompare(0, 0);
|
32
js/src/tests/ecma_6/LexicalEnvironment/unscopables-strict.js
Normal file
32
js/src/tests/ecma_6/LexicalEnvironment/unscopables-strict.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Strict assignment to the name of a property that's masked by @@unscopables
|
||||
// throws a ReferenceError.
|
||||
|
||||
let env = {k: 1};
|
||||
let f;
|
||||
with (env) {
|
||||
f = function () {
|
||||
"use strict";
|
||||
k = 2;
|
||||
};
|
||||
}
|
||||
|
||||
f();
|
||||
assertEq(env.k, 2);
|
||||
|
||||
env[Symbol.unscopables] = {k: true};
|
||||
assertThrowsInstanceOf(f, ReferenceError);
|
||||
|
||||
// @@unscopables is tested when the LHS of assignment is evaluated, so there is
|
||||
// no effect on the assignment if it is changed while evaluating the RHS.
|
||||
let g;
|
||||
with (env) {
|
||||
g = function () {
|
||||
"use strict";
|
||||
k = (env[Symbol.unscopables].k = true, 3);
|
||||
}
|
||||
}
|
||||
env[Symbol.unscopables].k = false;
|
||||
g();
|
||||
assertEq(env.k, 3);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,9 @@
|
|||
// Accessing an uninitialized variable due to @@unscopables is still a ReferenceError.
|
||||
|
||||
with ({x: 1, [Symbol.unscopables]: {x: true}})
|
||||
assertThrowsInstanceOf(() => x, ReferenceError);
|
||||
|
||||
let x;
|
||||
|
||||
reportCompare(0, 0);
|
||||
|
|
@ -49,6 +49,7 @@
|
|||
macro(construct, construct, "construct") \
|
||||
macro(constructor, constructor, "constructor") \
|
||||
macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
|
||||
macro(copyWithin, copyWithin, "copyWithin") \
|
||||
macro(count, count, "count") \
|
||||
macro(currency, currency, "currency") \
|
||||
macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
|
||||
|
@ -73,6 +74,7 @@
|
|||
macro(encodeURI, encodeURI, "encodeURI") \
|
||||
macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
|
||||
macro(endTimestamp, endTimestamp, "endTimestamp") \
|
||||
macro(entries, entries, "entries") \
|
||||
macro(enumerable, enumerable, "enumerable") \
|
||||
macro(enumerate, enumerate, "enumerate") \
|
||||
macro(escape, escape, "escape") \
|
||||
|
@ -81,6 +83,9 @@
|
|||
macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
|
||||
macro(fieldTypes, fieldTypes, "fieldTypes") \
|
||||
macro(fileName, fileName, "fileName") \
|
||||
macro(fill, fill, "fill") \
|
||||
macro(find, find, "find") \
|
||||
macro(findIndex, findIndex, "findIndex") \
|
||||
macro(fix, fix, "fix") \
|
||||
macro(flags, flags, "flags") \
|
||||
macro(float32, float32, "float32") \
|
||||
|
@ -106,6 +111,7 @@
|
|||
macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
|
||||
macro(ignoreCase, ignoreCase, "ignoreCase") \
|
||||
macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \
|
||||
macro(includes, includes, "includes") \
|
||||
macro(index, index, "index") \
|
||||
macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
|
||||
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
|
||||
|
@ -270,6 +276,7 @@
|
|||
macro(match, match, "match") \
|
||||
macro(species, species, "species") \
|
||||
macro(toPrimitive, toPrimitive, "toPrimitive") \
|
||||
macro(unscopables, unscopables, "unscopables") \
|
||||
/* Same goes for the descriptions of the well-known symbols. */ \
|
||||
macro(Symbol_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \
|
||||
macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \
|
||||
|
@ -277,7 +284,6 @@
|
|||
macro(Symbol_match, Symbol_match, "Symbol.match") \
|
||||
macro(Symbol_species, Symbol_species, "Symbol.species") \
|
||||
macro(Symbol_toPrimitive, Symbol_toPrimitive, "Symbol.toPrimitive") \
|
||||
macro(Symbol_toStringTag, Symbol_toStringTag, "Symbol.toStringTag") \
|
||||
macro(Symbol_unscopables, Symbol_unscopables, "Symbol.unscopables") \
|
||||
/* Function names for properties named by symbols. */ \
|
||||
macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \
|
||||
|
|
|
@ -8174,6 +8174,14 @@ DebuggerEnv_getVariable(JSContext* cx, unsigned argc, Value* vp)
|
|||
/* This can trigger getters. */
|
||||
ErrorCopier ec(ac);
|
||||
|
||||
bool found;
|
||||
if (!HasProperty(cx, env, id, &found))
|
||||
return false;
|
||||
if (!found) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// For DebugScopeObjects, we get sentinel values for optimized out
|
||||
// slots and arguments instead of throwing (the default behavior).
|
||||
//
|
||||
|
|
|
@ -1103,13 +1103,12 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
|
|||
Handle<GlobalObject*> global,
|
||||
JSCompartment* dest)
|
||||
{
|
||||
// Finish any ongoing incremental GC that may affect the destination zone.
|
||||
if (JS::IsIncrementalGCInProgress(rt) && dest->zone()->wasGCStarted())
|
||||
JS::FinishIncrementalGC(rt, JS::gcreason::API);
|
||||
|
||||
// After we call LeaveParseTaskZone() it's not safe to GC until we have
|
||||
// finished merging the contents of the parse task's compartment into the
|
||||
// destination compartment.
|
||||
// destination compartment. Finish any ongoing incremental GC first and
|
||||
// assert that no allocation can occur. (Bug 1371908 changes this, but
|
||||
// generates assertion failures.)
|
||||
gc::AutoFinishGC finishGC(rt);
|
||||
JS::AutoAssertNoAlloc noAlloc(rt);
|
||||
|
||||
LeaveParseTaskZone(rt, parseTask);
|
||||
|
|
|
@ -443,10 +443,9 @@ namespace js {
|
|||
*/
|
||||
struct WellKnownSymbols
|
||||
{
|
||||
js::ImmutableSymbolPtr iterator;
|
||||
js::ImmutableSymbolPtr match;
|
||||
js::ImmutableSymbolPtr species;
|
||||
js::ImmutableSymbolPtr toPrimitive;
|
||||
#define DECLARE_SYMBOL(name) js::ImmutableSymbolPtr name;
|
||||
JS_FOR_EACH_WELL_KNOWN_SYMBOL(DECLARE_SYMBOL)
|
||||
#undef DECLARE_SYMBOL
|
||||
|
||||
const ImmutableSymbolPtr& get(size_t u) const {
|
||||
MOZ_ASSERT(u < JS::WellKnownSymbolLimit);
|
||||
|
|
|
@ -652,6 +652,26 @@ DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclo
|
|||
return obj;
|
||||
}
|
||||
|
||||
/* Implements ES6 8.1.1.2.1 HasBinding steps 7-9. */
|
||||
static bool
|
||||
CheckUnscopables(JSContext *cx, HandleObject obj, HandleId id, bool *scopable)
|
||||
{
|
||||
RootedId unscopablesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols()
|
||||
.get(JS::SymbolCode::unscopables)));
|
||||
RootedValue v(cx);
|
||||
if (!GetProperty(cx, obj, obj, unscopablesId, &v))
|
||||
return false;
|
||||
if (v.isObject()) {
|
||||
RootedObject unscopablesObj(cx, &v.toObject());
|
||||
if (!GetProperty(cx, unscopablesObj, unscopablesObj, id, &v))
|
||||
return false;
|
||||
*scopable = !ToBoolean(v);
|
||||
} else {
|
||||
*scopable = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandleObject objp, MutableHandleShape propp)
|
||||
|
@ -662,7 +682,19 @@ with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|||
return true;
|
||||
}
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return LookupProperty(cx, actual, id, objp, propp);
|
||||
if (!LookupProperty(cx, actual, id, objp, propp))
|
||||
return false;
|
||||
|
||||
if (propp) {
|
||||
bool scopable;
|
||||
if (!CheckUnscopables(cx, actual, id, &scopable))
|
||||
return false;
|
||||
if (!scopable) {
|
||||
objp.set(nullptr);
|
||||
propp.set(nullptr);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -679,7 +711,15 @@ with_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
|
|||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return HasProperty(cx, actual, id, foundp);
|
||||
|
||||
// ES 8.1.1.2.1 step 3-5.
|
||||
if (!HasProperty(cx, actual, id, foundp))
|
||||
return false;
|
||||
if (!*foundp)
|
||||
return true;
|
||||
|
||||
// Steps 7-10. (Step 6 is a no-op.)
|
||||
return CheckUnscopables(cx, actual, id, foundp);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -2173,12 +2213,25 @@ class DebugScopeProxy : public BaseProxyHandler
|
|||
// target object, the object would indicate that native enumeration is
|
||||
// the thing to do, but native enumeration over the DynamicWithObject
|
||||
// wrapper yields no properties. So instead here we hack around the
|
||||
// issue, and punch a hole through to the with object target.
|
||||
Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>()
|
||||
? &scope->as<DynamicWithObject>().object() : scope));
|
||||
// issue: punch a hole through to the with object target, then manually
|
||||
// examine @@unscopables.
|
||||
bool isWith = scope->is<DynamicWithObject>();
|
||||
Rooted<JSObject*> target(cx, (isWith ? &scope->as<DynamicWithObject>().object() : scope));
|
||||
if (!GetPropertyKeys(cx, target, JSITER_OWNONLY, &props))
|
||||
return false;
|
||||
|
||||
if (isWith) {
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < props.length(); i++) {
|
||||
bool inScope;
|
||||
if (!CheckUnscopables(cx, scope, props[i], &inScope))
|
||||
return false;
|
||||
if (inScope)
|
||||
props[j++].set(props[i]);
|
||||
}
|
||||
props.resize(j);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function scopes are optimized to not contain unaliased variables so
|
||||
* they must be manually appended here.
|
||||
|
|
|
@ -258,7 +258,9 @@ XPCArrayHomogenizer::GetTypeForArray(JSContext* cx, HandleObject array,
|
|||
|
||||
bool XPCVariant::InitializeData(JSContext* cx)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
if (MOZ_UNLIKELY(!js::CheckRecursion(cx))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue val(cx, GetJSVal());
|
||||
|
||||
|
|
|
@ -755,10 +755,13 @@ nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
|
|||
} else {
|
||||
MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
|
||||
"Should use aBorderArea for box-decoration-break:clone");
|
||||
MOZ_ASSERT(aForFrame->GetSkipSides().IsEmpty(),
|
||||
MOZ_ASSERT(aForFrame->GetSkipSides().IsEmpty() ||
|
||||
IS_TRUE_OVERFLOW_CONTAINER(aForFrame),
|
||||
"Should not skip sides for box-decoration-break:clone except "
|
||||
"::first-letter/line continuations or other frame types that "
|
||||
"don't have borders but those shouldn't reach this point.");
|
||||
"don't have borders but those shouldn't reach this point. "
|
||||
"Overflow containers do reach this point though.");
|
||||
border.ApplySkipSides(aSkipSides);
|
||||
}
|
||||
|
||||
// Convert to dev pixels.
|
||||
|
|
|
@ -415,8 +415,10 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
|
|||
|
||||
// Get the border values
|
||||
WritingMode outerWM = aReflowState.GetWritingMode();
|
||||
const LogicalMargin border(outerWM,
|
||||
aReflowState.mStyleBorder->GetComputedBorder());
|
||||
const LogicalMargin border(outerWM, aDelegatingFrame->GetUsedBorder());
|
||||
// see https://hg.mozilla.org/mozilla-central/file/fe69c6a1b4a3/layout/generic/nsAbsoluteContainingBlock.cpp
|
||||
// for various backbugs XXX
|
||||
|
||||
const LogicalMargin margin =
|
||||
kidReflowState.ComputedLogicalMargin().ConvertTo(outerWM, wm);
|
||||
bool constrainBSize = (aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE)
|
||||
|
|
|
@ -1011,7 +1011,8 @@ nsIFrame::Sides
|
|||
nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const
|
||||
{
|
||||
if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
|
||||
NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
|
||||
NS_STYLE_BOX_DECORATION_BREAK_CLONE) &&
|
||||
!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
|
||||
return Sides();
|
||||
}
|
||||
|
||||
|
|
|
@ -1180,4 +1180,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
|
||||
static const int32_t kUnknownId = -1;
|
||||
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1518725677736000);
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1522005531140000);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -115,6 +115,9 @@ public:
|
|||
// perform its associated action. This is currently only relevant for
|
||||
// wheel and touch events.
|
||||
bool mHandledByAPZ : 1;
|
||||
// True if the event is currently being handled by an event listener that
|
||||
// was registered as a passive listener.
|
||||
bool mInPassiveListener: 1;
|
||||
|
||||
// If the event is being handled in target phase, returns true.
|
||||
inline bool InTargetPhase() const
|
||||
|
|
|
@ -1229,7 +1229,9 @@ IncrementalFinalizeRunnable::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#if(0)
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
#endif
|
||||
ReleaseNow(true);
|
||||
|
||||
if (mDeferredFinalizeFunctions.Length()) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user