diff --git a/dom/base/WindowNamedPropertiesHandler.cpp b/dom/base/WindowNamedPropertiesHandler.cpp index 64cfe9c77f..a8550ebdc3 100644 --- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/WindowBinding.h" #include "nsContentUtils.h" #include "nsDOMClassInfo.h" +#include "nsDOMWindowList.h" #include "nsGlobalWindow.h" #include "nsHTMLDocument.h" #include "nsJSUtils.h" @@ -181,14 +182,28 @@ WindowNamedPropertiesHandler::ownPropNames(JSContext* aCx, // Grab the DOM window. nsGlobalWindow* win = xpc::WindowOrNull(JS_GetGlobalForObject(aCx, aProxy)); nsTArray names; - win->GetSupportedNames(names); - // Filter out the ones we wouldn't expose from getOwnPropertyDescriptor. - // We iterate backwards so we can remove things from the list easily. - for (size_t i = names.Length(); i > 0; ) { - --i; // Now we're pointing at the next name we want to look at - nsIDOMWindow* childWin = win->GetChildWindow(names[i]); - if (!childWin || !ShouldExposeChildWindow(names[i], childWin)) { - names.RemoveElementAt(i); + // The names live on the outer window, which might be null + nsGlobalWindow* outer = win->GetOuterWindowInternal(); + if (outer) { + nsDOMWindowList* childWindows = outer->GetWindowList(); + uint32_t length = childWindows->GetLength(); + for (uint32_t i = 0; i < length; ++i) { + nsCOMPtr item = + childWindows->GetDocShellTreeItemAt(i); + // This is a bit silly, since we could presumably just do + // item->GetWindow(). But it's not obvious whether this does the same + // thing as GetChildWindow() with the item's name (due to the complexity + // of FindChildWithName). Since GetChildWindow is what we use in + // getOwnPropDescriptor, let's try to be consistent. + nsString name; + item->GetName(name); + if (!names.Contains(name)) { + // Make sure we really would expose it from getOwnPropDescriptor. + nsCOMPtr childWin = win->GetChildWindow(name); + if (childWin && ShouldExposeChildWindow(name, childWin)) { + names.AppendElement(name); + } + } } } if (!AppendNamedPropertyIds(aCx, aProxy, names, false, aProps)) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 7575aed963..7afe07ea48 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1141,6 +1141,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mIsPopupSpam(false), mBlockScriptedClosingFlag(false), mWasOffline(false), + mHasHadSlowScript(false), mNotifyIdleObserversIdleOnThaw(false), mNotifyIdleObserversActiveOnThaw(false), mCreatingInnerWindow(false), @@ -1209,6 +1210,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) Preferences::AddStrongObserver(mObserver, "intl.accept_languages"); } + + InitializeShowFocusRings(); } else { // |this| is an outer window. Outer windows start out frozen and // remain frozen until they get an inner window, so freeze this @@ -4307,23 +4310,6 @@ nsGlobalWindow::IndexedGetter(uint32_t aIndex) MOZ_CRASH(); } -void -nsGlobalWindow::GetSupportedNames(nsTArray& aNames) -{ - FORWARD_TO_OUTER_VOID(GetSupportedNames, (aNames)); - - nsDOMWindowList* windows = GetWindowList(); - if (windows) { - uint32_t length = windows->GetLength(); - nsString* name = aNames.AppendElements(length); - for (uint32_t i = 0; i < length; ++i, ++name) { - nsCOMPtr item = - windows->GetDocShellTreeItemAt(i); - item->GetName(*name); - } - } -} - bool nsGlobalWindow::DoResolve(JSContext* aCx, JS::Handle aObj, JS::Handle aId, @@ -7978,20 +7964,20 @@ nsGlobalWindow::MozRequestOverfill(OverfillCallback& aCallback, } void -nsGlobalWindow::ClearTimeout(int32_t aHandle, ErrorResult& aError) +nsGlobalWindow::ClearTimeout(int32_t aHandle) { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (aHandle > 0) { - ClearTimeoutOrInterval(aHandle, aError); + ClearTimeoutOrInterval(aHandle); } } void -nsGlobalWindow::ClearInterval(int32_t aHandle, ErrorResult& aError) +nsGlobalWindow::ClearInterval(int32_t aHandle) { if (aHandle > 0) { - ClearTimeoutOrInterval(aHandle, aError); + ClearTimeoutOrInterval(aHandle); } } @@ -10085,6 +10071,24 @@ nsGlobalWindow::SetActive(bool aActive) } } +bool +nsGlobalWindow::IsTopLevelWindowActive() +{ + nsCOMPtr treeItem(GetDocShell()); + if (!treeItem) { + return false; + } + + nsCOMPtr rootItem; + treeItem->GetRootTreeItem(getter_AddRefs(rootItem)); + if (!rootItem) { + return false; + } + + nsCOMPtr domWindow = rootItem->GetWindow(); + return domWindow && domWindow->IsActive(); +} + void nsGlobalWindow::SetIsBackground(bool aIsBackground) { MOZ_ASSERT(IsOuterWindow()); @@ -10173,6 +10177,15 @@ static bool IsLink(nsIContent* aContent) nsGkAtoms::simple, eCaseMatters)); } +static bool ShouldShowFocusRingIfFocusedByMouse(nsIContent* aNode) +{ + if (!aNode) { + return true; + } + return !IsLink(aNode) && + !aNode->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio); +} + void nsGlobalWindow::SetFocusedNode(nsIContent* aNode, uint32_t aFocusMethod, @@ -10206,9 +10219,10 @@ nsGlobalWindow::SetFocusedNode(nsIContent* aNode, // otherwise, we set mShowFocusRingForContent, as we don't want this to // be permanent for the window. On Windows, focus rings are only shown // when the FLAG_SHOWRING flag is used. On other platforms, focus rings - // are only hidden for clicks on links. + // are only visible on some elements. #ifndef XP_WIN - !(mFocusMethod & nsIFocusManager::FLAG_BYMOUSE) || !IsLink(aNode) || + !(mFocusMethod & nsIFocusManager::FLAG_BYMOUSE) || + ShouldShowFocusRingIfFocusedByMouse(aNode) || #endif aFocusMethod & nsIFocusManager::FLAG_SHOWRING) { mShowFocusRingForContent = true; @@ -10227,6 +10241,17 @@ nsGlobalWindow::GetFocusMethod() return mFocusMethod; } +void +nsGlobalWindow::InitializeShowFocusRings() +{ + nsPIDOMWindow* root = GetPrivateRoot(); + if (root) { + bool showAccelerators = false, showFocusRings = false; + root->GetKeyboardIndicators(&showAccelerators, &showFocusRings); + mShowFocusRings = showFocusRings; + } +} + bool nsGlobalWindow::ShouldShowFocusRing() { @@ -10331,15 +10356,6 @@ nsGlobalWindow::SetReadyForFocus() bool oldNeedsFocus = mNeedsFocus; mNeedsFocus = false; - // update whether focus rings need to be shown using the state from the - // root window - nsPIDOMWindow* root = GetPrivateRoot(); - if (root) { - bool showAccelerators, showFocusRings; - root->GetKeyboardIndicators(&showAccelerators, &showFocusRings); - mShowFocusRings = showFocusRings; - } - nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm) fm->WindowShown(this, oldNeedsFocus); @@ -11252,6 +11268,13 @@ nsGlobalWindow::ShowSlowScriptDialog() unsigned lineno; bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno); + // Record the slow script event if we haven't done so already for this inner window + // (which represents a particular page to the user). + if (!mHasHadSlowScript) { + Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1); + } + mHasHadSlowScript = true; + if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) { ProcessHangMonitor::SlowScriptAction action; @@ -12520,6 +12543,7 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout, reason = "setTimeout handler"; } + bool abortIntervalHandler = false; nsCOMPtr handler(timeout->mScriptHandler); RefPtr callback = handler->GetCallback(); if (!callback) { @@ -12539,17 +12563,35 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout, options.setFileAndLine(filename, lineNo) .setVersion(JSVERSION_DEFAULT); JS::Rooted global(aes.cx(), FastGetGlobalJSObject()); - nsJSUtils::EvaluateString(aes.cx(), nsDependentString(script), - global, options); + nsresult rv = + nsJSUtils::EvaluateString(aes.cx(), nsDependentString(script), + global, options); + if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) { + abortIntervalHandler = true; + } } else { // Hold strong ref to ourselves while we call the callback. nsCOMPtr me(static_cast(this)); - ErrorResult ignored; + ErrorResult rv; JS::Rooted ignoredVal(CycleCollectedJSRuntime::Get()->Runtime()); - callback->Call(me, handler->GetArgs(), &ignoredVal, ignored, reason); - ignored.SuppressException(); + callback->Call(me, handler->GetArgs(), &ignoredVal, rv, reason); + if (rv.IsUncatchableException()) { + abortIntervalHandler = true; + } + + rv.SuppressException(); } + // If we received an uncatchable exception, do not schedule the timeout again. + // This allows the slow script dialog to break easy DoS attacks like + // setInterval(function() { while(1); }, 100); + if (abortIntervalHandler) { + // If it wasn't an interval timer to begin with, this does nothing. If it + // was, we'll treat it as a timeout that we just ran and discard it when + // we return. + timeout->mIsInterval = false; + } + // We ignore any failures from calling EvaluateString() on the context or // Call() on a Function here since we're in a loop // where we're likely to be running timeouts whose OS timers @@ -12825,7 +12867,7 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout) } void -nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID, ErrorResult& aError) +nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID) { MOZ_RELEASE_ASSERT(IsInnerWindow()); @@ -14412,8 +14454,7 @@ nsGlobalWindow::GetExternal(ErrorResult& aRv) if (!mExternal) { AutoJSContext cx; JS::Rooted jsImplObj(cx); - ConstructJSImplementation(cx, "@mozilla.org/sidebar;1", - this, &jsImplObj, aRv); + ConstructJSImplementation("@mozilla.org/sidebar;1", this, &jsImplObj, aRv); if (aRv.Failed()) { return nullptr; } diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 508080dee2..916b68cef1 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -405,6 +405,7 @@ public: // Outer windows only. virtual void ActivateOrDeactivate(bool aActivate) override; virtual void SetActive(bool aActive) override; + virtual bool IsTopLevelWindowActive() override; virtual void SetIsBackground(bool aIsBackground) override; virtual void SetChromeEventHandler(mozilla::dom::EventTarget* aChromeEventHandler) override; @@ -479,8 +480,6 @@ public: already_AddRefed IndexedGetterOuter(uint32_t aIndex); already_AddRefed IndexedGetter(uint32_t aIndex); - void GetSupportedNames(nsTArray& aNames); - static bool IsPrivilegedChromeWindow(JSContext* /* unused */, JSObject* aObj); static bool IsShowModalDialogEnabled(JSContext* /* unused */ = nullptr, @@ -934,7 +933,7 @@ public: int32_t aTimeout, const mozilla::dom::Sequence& /* unused */, mozilla::ErrorResult& aError); - void ClearTimeout(int32_t aHandle, mozilla::ErrorResult& aError); + void ClearTimeout(int32_t aHandle); int32_t SetInterval(JSContext* aCx, mozilla::dom::Function& aFunction, const mozilla::dom::Optional& aTimeout, const mozilla::dom::Sequence& aArguments, @@ -943,7 +942,7 @@ public: const mozilla::dom::Optional& aTimeout, const mozilla::dom::Sequence& /* unused */, mozilla::ErrorResult& aError); - void ClearInterval(int32_t aHandle, mozilla::ErrorResult& aError); + void ClearInterval(int32_t aHandle); void Atob(const nsAString& aAsciiBase64String, nsAString& aBinaryData, mozilla::ErrorResult& aError); void Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String, @@ -1377,7 +1376,7 @@ public: // |interval| is in milliseconds. nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, int32_t interval, - bool aIsInterval, int32_t* aReturn) override; + bool aIsInterval, int32_t* aReturn); int32_t SetTimeoutOrInterval(JSContext* aCx, mozilla::dom::Function& aFunction, int32_t aTimeout, @@ -1386,14 +1385,7 @@ public: int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler, int32_t aTimeout, bool aIsInterval, mozilla::ErrorResult& aError); - void ClearTimeoutOrInterval(int32_t aTimerID, - mozilla::ErrorResult& aError); - nsresult ClearTimeoutOrInterval(int32_t aTimerID) override - { - mozilla::ErrorResult rv; - ClearTimeoutOrInterval(aTimerID, rv); - return rv.StealNSResult(); - } + void ClearTimeoutOrInterval(int32_t aTimerID); // JS specific timeout functions (JS args grabbed from context). nsresult ResetTimersForNonBackgroundWindow(); @@ -1550,6 +1542,8 @@ protected: inline int32_t DOMMinTimeoutValue() const; + void InitializeShowFocusRings(); + // Clear the document-dependent slots on our JS wrapper. Inner windows only. void ClearDocumentDependentSlots(JSContext* aCx); @@ -1559,9 +1553,11 @@ protected: const RefPtr& aEvent, mozilla::ErrorResult& aRv); +public: // Outer windows only. nsDOMWindowList* GetWindowList(); +protected: // Helper for getComputedStyle and getDefaultComputedStyle already_AddRefed GetComputedStyleHelperOuter(mozilla::dom::Element& aElt, @@ -1645,6 +1641,11 @@ protected: // Window offline status. Checked to see if we need to fire offline event bool mWasOffline : 1; + // Represents whether the inner window's page has had a slow script notice. + // Only used by inner windows; will always be false for outer windows. + // This is used to implement Telemetry measures such as SLOW_SCRIPT_PAGE_COUNT. + bool mHasHadSlowScript : 1; + // Track what sorts of events we need to fire when thawed bool mNotifyIdleObserversIdleOnThaw : 1; bool mNotifyIdleObserversActiveOnThaw : 1; diff --git a/dom/base/nsHTMLContentSerializer.cpp b/dom/base/nsHTMLContentSerializer.cpp index e12d7440a2..4bd3366b24 100644 --- a/dom/base/nsHTMLContentSerializer.cpp +++ b/dom/base/nsHTMLContentSerializer.cpp @@ -401,50 +401,66 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement, } static const uint16_t kValNBSP = 160; -static const char* kEntities[] = { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "&", nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - "<", nullptr, ">", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - " " + +#define _ 0 + +// This table indexes into kEntityStrings[]. +static const uint8_t kEntities[] = { + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, 2, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + 3, _, 4, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + 5 }; -static const char* kAttrEntities[] = { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, """, nullptr, nullptr, nullptr, "&", nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - "<", nullptr, ">", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - " " +// This table indexes into kEntityStrings[]. +static const uint8_t kAttrEntities[] = { + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, 1, _, _, _, 2, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + 3, _, 4, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + 5 +}; + +#undef _ + +static const char* const kEntityStrings[] = { + /* 0 */ nullptr, + /* 1 */ """, + /* 2 */ "&", + /* 3 */ "<", + /* 4 */ ">", + /* 5 */ " " }; uint32_t FindNextBasicEntity(const nsAString& aStr, const uint32_t aLen, uint32_t aIndex, - const char** aEntityTable, + const uint8_t* aEntityTable, const char** aEntity) { for (; aIndex < aLen; ++aIndex) { @@ -452,7 +468,7 @@ uint32_t FindNextBasicEntity(const nsAString& aStr, // needs to be replaced char16_t val = aStr[aIndex]; if (val <= kValNBSP && aEntityTable[val]) { - *aEntity = aEntityTable[val]; + *aEntity = kEntityStrings[aEntityTable[val]]; return aIndex; } } @@ -478,7 +494,7 @@ nsHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, if (!nonBasicEntities && (mFlags & (nsIDocumentEncoder::OutputEncodeBasicEntities))) { - const char **entityTable = mInAttribute ? kAttrEntities : kEntities; + const uint8_t* entityTable = mInAttribute ? kAttrEntities : kEntities; uint32_t start = 0; const uint32_t len = aStr.Length(); for (uint32_t i = 0; i < len; ++i) { @@ -510,7 +526,7 @@ nsHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, uint32_t advanceLength = 0; nsReadingIterator iter; - const char **entityTable = mInAttribute ? kAttrEntities : kEntities; + const uint8_t* entityTable = mInAttribute ? kAttrEntities : kEntities; nsAutoCString entityReplacement; for (aStr.BeginReading(iter); @@ -532,7 +548,7 @@ nsHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, for (; c < fragmentEnd; c++, advanceLength++) { char16_t val = *c; if (val <= kValNBSP && entityTable[val]) { - fullConstEntityText = entityTable[val]; + fullConstEntityText = kEntityStrings[entityTable[val]]; break; } else if (val > 127 && ((val < 256 && diff --git a/dom/base/nsHostObjectURI.cpp b/dom/base/nsHostObjectURI.cpp index 0bbbef8a07..8a840df716 100644 --- a/dom/base/nsHostObjectURI.cpp +++ b/dom/base/nsHostObjectURI.cpp @@ -85,15 +85,6 @@ nsHostObjectURI::Write(nsIObjectOutputStream* aStream) true); } -NS_IMETHODIMP -nsHostObjectURI::SetScheme(const nsACString& aScheme) -{ - // Disallow setting the scheme, since that could cause us to be associated - // with a different protocol handler that doesn't expect us to be carrying - // around a principal with nsIURIWithPrincipal. - return NS_ERROR_FAILURE; -} - // nsIIPCSerializableURI methods: void nsHostObjectURI::Serialize(mozilla::ipc::URIParams& aParams) @@ -144,6 +135,15 @@ nsHostObjectURI::Deserialize(const mozilla::ipc::URIParams& aParams) return mPrincipal != nullptr; } +NS_IMETHODIMP +nsHostObjectURI::SetScheme(const nsACString& aScheme) +{ + // Disallow setting the scheme, since that could cause us to be associated + // with a different protocol handler that doesn't expect us to be carrying + // around a principal with nsIURIWithPrincipal. + return NS_ERROR_FAILURE; +} + // nsIURI methods: nsresult nsHostObjectURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode, diff --git a/dom/base/nsIDocumentEncoder.idl b/dom/base/nsIDocumentEncoder.idl index f983b87d2d..4be2c108c8 100644 --- a/dom/base/nsIDocumentEncoder.idl +++ b/dom/base/nsIDocumentEncoder.idl @@ -35,7 +35,7 @@ interface nsIDocumentEncoderNodeFixup : nsISupports nsIDOMNode fixupNode(in nsIDOMNode aNode, out boolean aSerializeCloneKids); }; -[scriptable, uuid(e5ec69d7-eaa7-4de7-986b-455e17c7f71a)] +[scriptable, uuid(21f112df-d96f-47da-bfcb-5331273003d1)] interface nsIDocumentEncoder : nsISupports { // Output methods flag bits. There are a frightening number of these, @@ -240,6 +240,13 @@ interface nsIDocumentEncoder : nsISupports */ const unsigned long OutputRubyAnnotation = (1 << 26); + /** + * Disallow breaking of long character strings. This is important + * for serializing e-mail which contains CJK strings. These must + * not be broken just as "normal" longs strings aren't broken. + */ + const unsigned long OutputDisallowLineBreaking = (1 << 27); + /** * Initialize with a pointer to the document and the mime type. * @param aDocument Document to encode. diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 3ab855c8dd..5c914d8804 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -222,7 +222,12 @@ nsJSUtils::EvaluateString(JSContext* aCx, } if (!ok) { - rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW; + if (JS_IsExceptionPending(aCx)) { + rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW; + } else { + rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE; + } + if (!aCompileOptions.noScriptRval) { aRetValue.setUndefined(); } diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index fd1083d220..ef310e1917 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -103,6 +103,8 @@ public: virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0; virtual nsresult UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0; + virtual bool IsTopLevelWindowActive() = 0; + // Outer windows only. virtual void SetActive(bool aActive) { @@ -331,14 +333,6 @@ public: virtual bool IsFrozen() const = 0; - // Add a timeout to this window. - virtual nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, - int32_t interval, - bool aIsInterval, int32_t *aReturn) = 0; - - // Clear a timeout from this window. - virtual nsresult ClearTimeoutOrInterval(int32_t aTimerID) = 0; - nsPIDOMWindow *GetOuterWindow() { return mIsInnerWindow ? mOuterWindow.get() : this; diff --git a/dom/base/nsPlainTextSerializer.cpp b/dom/base/nsPlainTextSerializer.cpp index e5bcd6b3b4..1accfa2bd7 100644 --- a/dom/base/nsPlainTextSerializer.cpp +++ b/dom/base/nsPlainTextSerializer.cpp @@ -144,7 +144,7 @@ nsPlainTextSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn, mWrapColumn = aWrapColumn; // Only create a linebreaker if we will handle wrapping. - if (MayWrap()) { + if (MayWrap() && MayBreakLines()) { mLineBreaker = nsContentUtils::LineBreaker(); } diff --git a/dom/base/nsPlainTextSerializer.h b/dom/base/nsPlainTextSerializer.h index a11adc1aa1..acdbff5bae 100644 --- a/dom/base/nsPlainTextSerializer.h +++ b/dom/base/nsPlainTextSerializer.h @@ -101,6 +101,10 @@ private: ((mFlags & nsIDocumentEncoder::OutputFormatted) || (mFlags & nsIDocumentEncoder::OutputWrap)); } + inline bool MayBreakLines() + { + return !(mFlags & nsIDocumentEncoder::OutputDisallowLineBreaking); + } inline bool DoOutput() { diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index b516d2f218..e51a881afd 100644 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -412,47 +412,6 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, return true; } - -bool -nsXHTMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement, - nsIAtom * aName, - int32_t aNamespaceID, - nsAString& aStr) -{ - // this method is not called by nsHTMLContentSerializer - // so we don't have to check HTML element, just XHTML - NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !"); - - if (kNameSpaceID_XHTML != aNamespaceID) { - return nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName, - aNamespaceID, aStr); - } - - nsIContent* content = aOriginalElement; - - // for non empty elements, even if they are not a container, we always - // serialize their content, because the XHTML element could contain non XHTML - // nodes useful in some context, like in an XSLT stylesheet - if (HasNoChildren(content)) { - - nsIParserService* parserService = nsContentUtils::GetParserService(); - - if (parserService) { - bool isContainer; - parserService-> - IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(aName), - isContainer); - if (!isContainer) { - // for backward compatibility with HTML 4 user agents - // only non-container HTML elements can be closed immediatly, - // and a space is added before /> - return AppendToString(NS_LITERAL_STRING(" />"), aStr); - } - } - } - return AppendToString(kGreaterThan, aStr); -} - bool nsXHTMLContentSerializer::AfterElementStart(nsIContent* aContent, nsIContent* aOriginalElement, @@ -552,52 +511,26 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent, } bool -nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent, - bool & aForceFormat, +nsXHTMLContentSerializer::CheckElementEnd(Element* aElement, + bool& aForceFormat, nsAString& aStr) { NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !"); aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) && - aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty); + aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty); - // this method is not called by nsHTMLContentSerializer - // so we don't have to check HTML element, just XHTML - if (aContent->IsHTMLElement()) { - if (mIsCopying && aContent->IsHTMLElement(nsGkAtoms::ol)) { - NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack"); - /* Though at this point we must always have an state to be deleted as all - the OL opening tags are supposed to push an olState object to the stack*/ - if (!mOLStateStack.IsEmpty()) { + if (mIsCopying && aElement->IsHTMLElement(nsGkAtoms::ol)) { + NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack"); + /* Though at this point we must always have an state to be deleted as all + the OL opening tags are supposed to push an olState object to the stack*/ + if (!mOLStateStack.IsEmpty()) { mOLStateStack.RemoveElementAt(mOLStateStack.Length() -1); - } } - - if (HasNoChildren(aContent)) { - nsIParserService* parserService = nsContentUtils::GetParserService(); - - if (parserService) { - bool isContainer; - - parserService-> - IsContainer(parserService->HTMLCaseSensitiveAtomTagToId( - aContent->NodeInfo()->NameAtom()), - isContainer); - if (!isContainer) { - // non-container HTML elements are already closed, - // see AppendEndOfElementStart - return false; - } - } - } - // for backward compatibility with old HTML user agents, - // empty elements should have an ending tag, so we mustn't call - // nsXMLContentSerializer::CheckElementEnd - return true; } bool dummyFormat; - return nsXMLContentSerializer::CheckElementEnd(aContent, dummyFormat, aStr); + return nsXMLContentSerializer::CheckElementEnd(aElement, dummyFormat, aStr); } bool diff --git a/dom/base/nsXHTMLContentSerializer.h b/dom/base/nsXHTMLContentSerializer.h index 0108ca7d99..6fc7dce669 100644 --- a/dom/base/nsXHTMLContentSerializer.h +++ b/dom/base/nsXHTMLContentSerializer.h @@ -47,20 +47,14 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer { nsAString& aStr, nsresult& aResult) override; - MOZ_WARN_UNUSED_RESULT - virtual bool AppendEndOfElementStart(nsIContent *aOriginalElement, - nsIAtom * aName, - int32_t aNamespaceID, - nsAString& aStr) override; - MOZ_WARN_UNUSED_RESULT virtual bool AfterElementStart(nsIContent* aContent, nsIContent* aOriginalElement, nsAString& aStr) override; - virtual bool CheckElementEnd(nsIContent * aContent, - bool & aForceFormat, - nsAString& aStr) override; + virtual bool CheckElementEnd(mozilla::dom::Element* aContent, + bool& aForceFormat, + nsAString& aStr) override; virtual void AfterElementEnd(nsIContent * aContent, nsAString& aStr) override; diff --git a/dom/base/nsXMLContentSerializer.cpp b/dom/base/nsXMLContentSerializer.cpp index 800ff5c752..247ba43c4b 100644 --- a/dom/base/nsXMLContentSerializer.cpp +++ b/dom/base/nsXMLContentSerializer.cpp @@ -113,6 +113,8 @@ nsXMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn, mDoWrap = (mFlags & nsIDocumentEncoder::OutputWrap && !mDoRaw); + mAllowLineBreaking = !(mFlags & nsIDocumentEncoder::OutputDisallowLineBreaking); + if (!aWrapColumn) { mMaxColumn = 72; } @@ -959,8 +961,7 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement, name, aStr, skipAttr, addNSAttr), NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(AppendEndOfElementStart(aOriginalElement, name, - content->GetNameSpaceID(), aStr), + NS_ENSURE_TRUE(AppendEndOfElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() @@ -973,19 +974,56 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement, return NS_OK; } +// aElement is the actual element we're outputting. aOriginalElement is the one +// in the original DOM, which is the one we have to test for kids. +static bool +ElementNeedsSeparateEndTag(Element* aElement, Element* aOriginalElement) +{ + if (aOriginalElement->GetChildCount()) { + // We have kids, so we need a separate end tag. This needs to be checked on + // aOriginalElement because that's the one that's actually in the DOM and + // might have kids. + return true; + } + + if (!aElement->IsHTMLElement()) { + // Empty non-HTML elements can just skip a separate end tag. + return false; + } + + // HTML container tags should have a separate end tag even if empty, per spec. + // See + // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm + bool isHTMLContainer = true; // Default in case we get no parser service. + nsIParserService* parserService = nsContentUtils::GetParserService(); + if (parserService) { + nsIAtom* localName = aElement->NodeInfo()->NameAtom(); + parserService->IsContainer( + parserService->HTMLCaseSensitiveAtomTagToId(localName), + isHTMLContainer); + } + return isHTMLContainer; +} + bool -nsXMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement, - nsIAtom * aName, - int32_t aNamespaceID, +nsXMLContentSerializer::AppendEndOfElementStart(Element* aElement, + Element* aOriginalElement, nsAString& aStr) { - // We don't output a separate end tag for empty elements - if (!aOriginalElement->GetChildCount()) { - return AppendToString(NS_LITERAL_STRING("/>"), aStr); - } - else { + if (ElementNeedsSeparateEndTag(aElement, aOriginalElement)) { return AppendToString(kGreaterThan, aStr); } + + // We don't need a separate end tag. For HTML elements (which at this point + // must be non-containers), append a space before the '/', per spec. See + // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm + if (aOriginalElement->IsHTMLElement()) { + if (!AppendToString(kSpace, aStr)) { + return false; + } + } + + return AppendToString(NS_LITERAL_STRING("/>"), aStr); } NS_IMETHODIMP @@ -997,7 +1035,7 @@ nsXMLContentSerializer::AppendElementEnd(Element* aElement, nsIContent* content = aElement; bool forceFormat = false, outputElementEnd; - outputElementEnd = CheckElementEnd(content, forceFormat, aStr); + outputElementEnd = CheckElementEnd(aElement, forceFormat, aStr); nsIAtom *name = content->NodeInfo()->NameAtom(); @@ -1118,13 +1156,17 @@ nsXMLContentSerializer::CheckElementStart(nsIContent * aContent, } bool -nsXMLContentSerializer::CheckElementEnd(nsIContent * aContent, - bool & aForceFormat, +nsXMLContentSerializer::CheckElementEnd(Element* aElement, + bool& aForceFormat, nsAString& aStr) { // We don't output a separate end tag for empty element aForceFormat = false; - return aContent->GetChildCount() > 0; + + // XXXbz this is a bit messed up, but by now we don't have our fixed-up + // version of aElement anymore. Let's hope fixup never changes the localName + // or namespace... + return ElementNeedsSeparateEndTag(aElement, aElement); } bool @@ -1151,24 +1193,39 @@ nsXMLContentSerializer::AppendToString(const nsAString& aStr, static const uint16_t kGTVal = 62; -static const char* kEntities[] = { - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "&", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "<", "", ">" + +#define _ 0 + +// This table indexes into kEntityStrings[]. +static const uint8_t kEntities[] = { + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, 2, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + 3, _, 4 }; -static const char* kAttrEntities[] = { - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", """, "", "", "", "&", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "<", "", ">" +// This table indexes into kEntityStrings[]. +static const uint8_t kAttrEntities[] = { + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, 1, _, _, _, 2, _, + _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, + 3, _, 4 +}; + +#undef _ + +static const char* const kEntityStrings[] = { + /* 0 */ nullptr, + /* 1 */ """, + /* 2 */ "&", + /* 3 */ "<", + /* 4 */ ">", }; bool @@ -1182,7 +1239,7 @@ nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, uint32_t advanceLength = 0; nsReadingIterator iter; - const char **entityTable = mInAttribute ? kAttrEntities : kEntities; + const uint8_t* entityTable = mInAttribute ? kAttrEntities : kEntities; for (aStr.BeginReading(iter); iter != done_reading; @@ -1198,8 +1255,8 @@ nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, // needs to be replaced for (; c < fragmentEnd; c++, advanceLength++) { char16_t val = *c; - if ((val <= kGTVal) && (entityTable[val][0] != 0)) { - entityText = entityTable[val]; + if ((val <= kGTVal) && entityTable[val]) { + entityText = kEntityStrings[entityTable[val]]; break; } } @@ -1540,23 +1597,25 @@ nsXMLContentSerializer::AppendWrapped_NonWhitespaceSequence( // we must wrap onceAgainBecauseWeAddedBreakInFront = false; bool foundWrapPosition = false; - int32_t wrapPosition; + int32_t wrapPosition = 0; - nsILineBreaker *lineBreaker = nsContentUtils::LineBreaker(); + if (mAllowLineBreaking) { + nsILineBreaker *lineBreaker = nsContentUtils::LineBreaker(); - wrapPosition = lineBreaker->Prev(aSequenceStart, - (aEnd - aSequenceStart), - (aPos - aSequenceStart) + 1); - if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { - foundWrapPosition = true; - } - else { - wrapPosition = lineBreaker->Next(aSequenceStart, + wrapPosition = lineBreaker->Prev(aSequenceStart, (aEnd - aSequenceStart), - (aPos - aSequenceStart)); + (aPos - aSequenceStart) + 1); if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { foundWrapPosition = true; } + else { + wrapPosition = lineBreaker->Next(aSequenceStart, + (aEnd - aSequenceStart), + (aPos - aSequenceStart)); + if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { + foundWrapPosition = true; + } + } } if (foundWrapPosition) { diff --git a/dom/base/nsXMLContentSerializer.h b/dom/base/nsXMLContentSerializer.h index 82cfc48b90..6c265825b2 100644 --- a/dom/base/nsXMLContentSerializer.h +++ b/dom/base/nsXMLContentSerializer.h @@ -233,14 +233,16 @@ class nsXMLContentSerializer : public nsIContentSerializer { nsresult& aResult); /** - * this method is responsible to finish the start tag, - * in particulary to append the "greater than" sign + * This method is responsible for appending the '>' at the end of the start + * tag, possibly preceded by '/' and maybe a ' ' before that too. + * + * aElement and aOriginalElement are the same as the corresponding arguments + * to AppendElementStart. */ MOZ_WARN_UNUSED_RESULT - virtual bool AppendEndOfElementStart(nsIContent *aOriginalElement, - nsIAtom * aName, - int32_t aNamespaceID, - nsAString& aStr); + bool AppendEndOfElementStart(mozilla::dom::Element* aEleemnt, + mozilla::dom::Element* aOriginalElement, + nsAString& aStr); /** * This method can be redefine to serialize additional things just after @@ -260,9 +262,9 @@ class nsXMLContentSerializer : public nsIContentSerializer { * by setting aForceFormat to true. * @return boolean true if the element can be output */ - virtual bool CheckElementEnd(nsIContent * aContent, - bool & aForceFormat, - nsAString& aStr); + virtual bool CheckElementEnd(mozilla::dom::Element* aElement, + bool& aForceFormat, + nsAString& aStr); /** * This method can be redefine to serialize additional things just after @@ -355,6 +357,9 @@ class nsXMLContentSerializer : public nsIContentSerializer { // true = wrapping should be done (OutputWrap flag) bool mDoWrap; + // true = we can break lines (OutputDisallowLineBreaking flag) + bool mAllowLineBreaking; + // number of maximum column in a line, in the wrap mode uint32_t mMaxColumn; diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 60296d1556..db96b43932 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -772,11 +772,13 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1 [test_processing_instruction_update_stylesheet.xhtml] [test_progress_events_for_gzip_data.html] [test_range_bounds.html] -skip-if = toolkit == 'android' || e10s +skip-if = toolkit == 'android' [test_reentrant_flush.html] -skip-if = toolkit == 'android' || e10s #RANDOM +skip-if = toolkit == 'android' +[test_setInterval_uncatchable_exception.html] +skip-if = debug == false [test_sync_xhr_timer.xhtml] -skip-if = toolkit == 'android' || e10s #RANDOM +skip-if = toolkit == 'android' [test_text_wholeText.html] [test_textnode_normalize_in_selection.html] [test_textnode_split_in_selection.html] diff --git a/dom/base/test/test_setInterval_uncatchable_exception.html b/dom/base/test/test_setInterval_uncatchable_exception.html new file mode 100644 index 0000000000..70a1d96c08 --- /dev/null +++ b/dom/base/test/test_setInterval_uncatchable_exception.html @@ -0,0 +1,55 @@ + + + + + + Test for Bug 1252268 + + + + +Mozilla Bug 1252268 + + + + + diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index d3c445a29f..175b19f91b 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -476,9 +476,8 @@ ErrorResult::SetPendingException(JSContext* cx) return; } if (IsJSContextException()) { - // Whatever we need to throw is on the JSContext already. We - // can't assert that there is a pending exception on it, though, - // because in the uncatchable exception case there won't be one. + // Whatever we need to throw is on the JSContext already. + MOZ_ASSERT(JS_IsExceptionPending(cx)); mResult = NS_OK; return; } @@ -513,6 +512,16 @@ ErrorResult::StealExceptionFromJSContext(JSContext* cx) JS_ClearPendingException(cx); } +void +ErrorResult::NoteJSContextException(JSContext* aCx) +{ + if (JS_IsExceptionPending(aCx)) { + mResult = NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT; + } else { + mResult = NS_ERROR_UNCATCHABLE_EXCEPTION; + } +} + namespace dom { bool @@ -2250,7 +2259,7 @@ GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle obj, } already_AddRefed -ConstructJSImplementation(JSContext* aCx, const char* aContractId, +ConstructJSImplementation(const char* aContractId, const GlobalObject& aGlobal, JS::MutableHandle aObject, ErrorResult& aRv) @@ -2262,7 +2271,7 @@ ConstructJSImplementation(JSContext* aCx, const char* aContractId, return nullptr; } - ConstructJSImplementation(aCx, aContractId, global, aObject, aRv); + ConstructJSImplementation(aContractId, global, aObject, aRv); if (aRv.Failed()) { return nullptr; @@ -2271,11 +2280,13 @@ ConstructJSImplementation(JSContext* aCx, const char* aContractId, } void -ConstructJSImplementation(JSContext* aCx, const char* aContractId, +ConstructJSImplementation(const char* aContractId, nsIGlobalObject* aGlobal, JS::MutableHandle aObject, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread()); + // Make sure to divorce ourselves from the calling JS while creating and // initializing the object, so exceptions from that will get reported // properly, since those are never exceptions that a spec wants to be thrown. @@ -2303,7 +2314,7 @@ ConstructJSImplementation(JSContext* aCx, const char* aContractId, nsCOMPtr gpi = do_QueryInterface(implISupports); if (gpi) { - JS::Rooted initReturn(aCx); + JS::Rooted initReturn(nsContentUtils::RootingCxForThread()); rv = gpi->Init(window, &initReturn); if (NS_FAILED(rv)) { aRv.Throw(rv); diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index b23884a441..bdf95f826b 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -2648,13 +2648,13 @@ GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle obj, nsIGlobalObject** global); void -ConstructJSImplementation(JSContext* aCx, const char* aContractId, +ConstructJSImplementation(const char* aContractId, nsIGlobalObject* aGlobal, JS::MutableHandle aObject, ErrorResult& aRv); already_AddRefed -ConstructJSImplementation(JSContext* aCx, const char* aContractId, +ConstructJSImplementation(const char* aContractId, const GlobalObject& aGlobal, JS::MutableHandle aObject, ErrorResult& aRv); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 7e78a5539f..c00bf1e3a7 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1275,6 +1275,10 @@ DOMInterfaces = { 'concrete': False, }, +'TestFunctions': { + 'wrapperCache': False +}, + 'Text': { # Total hack to allow binding code to realize that nsTextNode can # in fact be cast to Text. diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index 553fa70d7b..3882c4a4a1 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -297,6 +297,12 @@ CallbackObject::CallSetup::~CallSetup() if (saved) { JS_RestoreFrameChain(mCx); } + + if (mErrorResult.IsJSContextException()) { + // XXXkhuey bug 1117269. + // This isn't true anymore ... so throw something else. + mErrorResult.Throw(NS_ERROR_UNEXPECTED); + } } } diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 6e0d601c8a..b501490193 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -14356,7 +14356,7 @@ def genConstructorBody(descriptor, initCall=""): """ JS::Rooted jsImplObj(cx); nsCOMPtr globalHolder = - ConstructJSImplementation(cx, "${contractId}", global, &jsImplObj, aRv); + ConstructJSImplementation("${contractId}", global, &jsImplObj, aRv); if (aRv.Failed()) { return nullptr; } @@ -15180,7 +15180,7 @@ class CallbackMethod(CallbackMember): $*{declThis} if (${callGuard}!JS::Call(cx, ${thisVal}, callable, ${args}, &rval)) { - aRv.Throw(NS_ERROR_UNEXPECTED); + aRv.NoteJSContextException(cx); return${errorReturn}; } """, diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index 405c712a1d..41d8863db3 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -236,9 +236,9 @@ public: // Flag on the ErrorResult that whatever needs throwing has been // thrown on the JSContext already and we should not mess with it. - void NoteJSContextException() { - mResult = NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT; - } + // If nothing was thrown, this becomes an uncatchable exception. + void NoteJSContextException(JSContext* aCx); + // Check whether the ErrorResult says to just throw whatever is on // the JSContext already. bool IsJSContextException() { diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index b1828e0f11..03d5b69dc6 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -100,6 +100,7 @@ SOURCES += [ # them are only run in debug mode. if CONFIG['MOZ_DEBUG']: EXPORTS.mozilla.dom += [ + "test/TestFunctions.h", "test/TestInterfaceIterableDouble.h", "test/TestInterfaceIterableSingle.h", "test/TestInterfaceMaplike.h", @@ -108,6 +109,7 @@ if CONFIG['MOZ_DEBUG']: "test/TestInterfaceSetlikeNode.h" ] UNIFIED_SOURCES += [ + "test/TestFunctions.cpp", "test/TestInterfaceIterableDouble.cpp", "test/TestInterfaceIterableSingle.cpp", "test/TestInterfaceMaplike.cpp", diff --git a/dom/bindings/test/TestFunctions.cpp b/dom/bindings/test/TestFunctions.cpp new file mode 100644 index 0000000000..9a8e29ec52 --- /dev/null +++ b/dom/bindings/test/TestFunctions.cpp @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/TestFunctions.h" + +namespace mozilla { +namespace dom { + +/* static */ void +TestFunctions::ThrowUncatchableException(GlobalObject& aGlobal, + ErrorResult& aRv) +{ + aRv.ThrowUncatchableException(); +} + +} +} diff --git a/dom/bindings/test/TestFunctions.h b/dom/bindings/test/TestFunctions.h new file mode 100644 index 0000000000..4e65b6ec21 --- /dev/null +++ b/dom/bindings/test/TestFunctions.h @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_TestFunctions_h +#define mozilla_dom_TestFunctions_h + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/NonRefcountedDOMObject.h" + +namespace mozilla { +namespace dom { + +class TestFunctions : public NonRefcountedDOMObject { +public: + static void + ThrowUncatchableException(GlobalObject& aGlobal, ErrorResult& aRv); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_TestFunctions_h diff --git a/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json index 8626f56fb0..2c63c08510 100644 --- a/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json +++ b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json @@ -1,4 +1,2 @@ { - "Static name on the prototype": true, - "constructor": true } diff --git a/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html b/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html index 1b1f746829..b01e24da98 100644 --- a/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html +++ b/dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html @@ -31,7 +31,7 @@ test(function() { assert_true("bar" in gsp, "bar in gsp"); assert_true(gsp.hasOwnProperty("bar"), "gsp.hasOwnProperty(\"bar\")"); assert_data_propdesc(Object.getOwnPropertyDescriptor(gsp, "bar"), - false, true, true); + true, false, true); }, "Static name on the prototype"); test(function() { assert_equals(window.constructor, Window); @@ -45,9 +45,8 @@ test(function() { var gsp = Object.getPrototypeOf(proto); assert_true("constructor" in gsp, "constructor in gsp"); - assert_true(gsp.hasOwnProperty("constructor"), "gsp.hasOwnProperty(\"constructor\")"); - assert_data_propdesc(Object.getOwnPropertyDescriptor(gsp, "constructor"), - false, true, true); + assert_false(gsp.hasOwnProperty("constructor"), "gsp.hasOwnProperty(\"constructor\")"); + assert_equals(Object.getOwnPropertyDescriptor(gsp, "constructor"), undefined) }, "constructor"); var t = async_test("Dynamic name") var t2 = async_test("Ghost name") diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 04907b7404..cb93c03cad 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -364,7 +364,7 @@ Promise::PromiseCapability::RejectWithException(JSContext* aCx, JS::Rooted ignored(aCx); if (!JS::Call(aCx, JS::UndefinedHandleValue, mReject, JS::HandleValueArray(exn), &ignored)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); } } @@ -537,7 +537,7 @@ Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, JS::Rooted p(aCx, JS::CallOriginalPromiseResolve(aCx, aValue)); if (!p) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return nullptr; } @@ -553,7 +553,7 @@ Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx, JS::Rooted p(aCx, JS::CallOriginalPromiseReject(aCx, aValue)); if (!p) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return nullptr; } @@ -576,7 +576,7 @@ Promise::All(const GlobalObject& aGlobal, JS::AutoObjectVector promises(cx); if (!promises.reserve(aPromiseList.Length())) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return nullptr; } @@ -584,7 +584,7 @@ Promise::All(const GlobalObject& aGlobal, JS::Rooted promiseObj(cx, promise->PromiseObj()); // Just in case, make sure these are all in the context compartment. if (!JS_WrapObject(cx, &promiseObj)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return nullptr; } promises.infallibleAppend(promiseObj); @@ -592,7 +592,7 @@ Promise::All(const GlobalObject& aGlobal, JS::Rooted result(cx, JS::GetWaitForAllPromise(cx, promises)); if (!result) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return nullptr; } @@ -615,7 +615,7 @@ Promise::Then(JSContext* aCx, // should be OK. JS::Rooted promise(aCx, PromiseObj()); if (!JS_WrapObject(aCx, &promise)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } @@ -623,7 +623,7 @@ Promise::Then(JSContext* aCx, if (aResolveCallback) { resolveCallback = aResolveCallback->Callback(); if (!JS_WrapObject(aCx, &resolveCallback)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } } @@ -632,7 +632,7 @@ Promise::Then(JSContext* aCx, if (aRejectCallback) { rejectCallback = aRejectCallback->Callback(); if (!JS_WrapObject(aCx, &rejectCallback)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } } @@ -641,7 +641,7 @@ Promise::Then(JSContext* aCx, retval = JS::CallOriginalPromiseThen(aCx, promise, resolveCallback, rejectCallback); if (!retval) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } @@ -1131,6 +1131,11 @@ Promise::CallInitFunction(const GlobalObject& aGlobal, aRv.WouldReportJSException(); if (aRv.Failed()) { + if (aRv.IsUncatchableException()) { + // Just propagate this to the caller. + return; + } + // There are two possibilities here. Either we've got a rethrown exception, // or we reported that already and synthesized a generic NS_ERROR_FAILURE on // the ErrorResult. In the former case, it doesn't much matter how we get @@ -1230,13 +1235,13 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, // the canonical Promise for that compartment actually makes sense. JS::Rooted constructorValue(aCx, aConstructor); if (!MaybeWrapObjectValue(aCx, &constructorValue)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx, global); if (!defaultCtor) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } if (defaultCtor == &constructorValue.toObject()) { @@ -1260,7 +1265,7 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, CreateFunction(aCx, aCapability.mNativePromise, PromiseCallback::Resolve); if (!resolveFuncObj) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } aCapability.mResolve.setObject(*resolveFuncObj); @@ -1269,7 +1274,7 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, CreateFunction(aCx, aCapability.mNativePromise, PromiseCallback::Reject); if (!rejectFuncObj) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } aCapability.mReject.setObject(*rejectFuncObj); @@ -1307,7 +1312,7 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, if (!JS::Construct(aCx, aConstructor, JS::HandleValueArray(getCapabilities), &promiseObj)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } @@ -1368,7 +1373,7 @@ Promise::Resolve(const GlobalObject& aGlobal, JS::Handle aThisv, if (NS_SUCCEEDED(rv)) { JS::Rooted constructor(cx); if (!JS_GetProperty(cx, valueObj, "constructor", &constructor)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return; } @@ -1400,7 +1405,7 @@ Promise::Resolve(const GlobalObject& aGlobal, JS::Handle aThisv, capability.mResolve, JS::HandleValueArray(value), &ignored)) { // Step 7. - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return; } } @@ -1465,7 +1470,7 @@ Promise::Reject(const GlobalObject& aGlobal, JS::Handle aThisv, capability.mReject, JS::HandleValueArray(value), &ignored)) { // Step 6. - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return; } } @@ -1505,7 +1510,7 @@ SpeciesConstructor(JSContext* aCx, JS::Rooted constructorVal(aCx); if (!JS_GetProperty(aCx, promise, "constructor", &constructorVal)) { // Step 3. - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } @@ -1528,7 +1533,7 @@ SpeciesConstructor(JSContext* aCx, JS::Rooted constructorObj(aCx, &constructorVal.toObject()); if (!JS_GetPropertyById(aCx, constructorObj, species, &speciesVal)) { // Step 7. - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } @@ -1560,7 +1565,7 @@ Promise::Then(JSContext* aCx, JS::Handle aCalleeGlobal, // Step 1. JS::Rooted promiseVal(aCx, JS::ObjectValue(*GetWrapper())); if (!MaybeWrapObjectValue(aCx, &promiseVal)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } JS::Rooted promiseObj(aCx, &promiseVal.toObject()); @@ -1578,13 +1583,13 @@ Promise::Then(JSContext* aCx, JS::Handle aCalleeGlobal, JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx, calleeGlobal); if (!defaultCtor) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } defaultCtorVal.setObject(*defaultCtor); } if (!MaybeWrapObjectValue(aCx, &defaultCtorVal)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } @@ -1598,7 +1603,7 @@ Promise::Then(JSContext* aCx, JS::Handle aCalleeGlobal, // Step 5. GlobalObject globalObj(aCx, GetWrapper()); if (globalObj.Failed()) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } nsCOMPtr globalObject = @@ -1697,7 +1702,7 @@ Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, // overridden Promise.prototype.then. JS::Rooted promiseVal(aCx, JS::ObjectValue(*GetWrapper())); if (!MaybeWrapObjectValue(aCx, &promiseVal)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } JS::Rooted promiseObj(aCx, &promiseVal.toObject()); @@ -1708,14 +1713,14 @@ Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, callbacks[1].setObject(*aRejectCallback->Callable()); // It could be in any compartment, so put it in ours. if (!MaybeWrapObjectValue(aCx, callbacks[1])) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); return; } } else { callbacks[1].setNull(); } if (!JS_CallFunctionName(aCx, promiseObj, "then", callbacks, aRetval)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(aCx); } } @@ -1975,7 +1980,7 @@ Promise::All(const GlobalObject& aGlobal, JS::Handle aThisv, // want to do that anyway. aRetval.set(capability.PromiseValue()); if (!MaybeWrapValue(cx, aRetval)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return; } @@ -2262,7 +2267,7 @@ Promise::Race(const GlobalObject& aGlobal, JS::Handle aThisv, // want to do that anyway. aRetval.set(capability.PromiseValue()); if (!MaybeWrapValue(cx, aRetval)) { - aRv.NoteJSContextException(); + aRv.NoteJSContextException(cx); return; } diff --git a/dom/promise/PromiseCallback.cpp b/dom/promise/PromiseCallback.cpp index 13ed1c53fd..c0c5e94c0a 100644 --- a/dom/promise/PromiseCallback.cpp +++ b/dom/promise/PromiseCallback.cpp @@ -341,6 +341,11 @@ WrapperPromiseCallback::Call(JSContext* aCx, // PromiseReactionTask step 7 if (rv.Failed()) { + if (rv.IsUncatchableException()) { + // We have nothing to resolve/reject the promise with. + return rv.StealNSResult(); + } + JS::Rooted value(aCx); { // Scope for JSAutoCompartment // Convert the ErrorResult to a JS exception object that we can reject diff --git a/dom/promise/tests/mochitest.ini b/dom/promise/tests/mochitest.ini index 1f2c0d5e08..4117076d03 100644 --- a/dom/promise/tests/mochitest.ini +++ b/dom/promise/tests/mochitest.ini @@ -1,10 +1,14 @@ [DEFAULT] # Support files for chrome tests that we want to load over HTTP need # to go in here, not chrome.ini, apparently. -support-files = file_promise_xrays.html +support-files = + file_promise_xrays.html + promise_uncatchable_exception.js [test_bug883683.html] [test_promise.html] +[test_promise_uncatchable_exception.html] +skip-if = debug == false [test_promise_utils.html] [test_resolve.html] [test_resolver_return_value.html] diff --git a/dom/promise/tests/promise_uncatchable_exception.js b/dom/promise/tests/promise_uncatchable_exception.js new file mode 100644 index 0000000000..d062e21af3 --- /dev/null +++ b/dom/promise/tests/promise_uncatchable_exception.js @@ -0,0 +1,9 @@ +postMessage("Done", "*"); + +var p = new Promise(function(resolve, reject) { + TestFunctions.throwUncatchableException(); + ok(false, "Shouldn't get here!"); +}).catch(function(exception) { + ok(false, "Shouldn't get here!"); +}); +ok(false, "Shouldn't get here!"); diff --git a/dom/promise/tests/test_promise_uncatchable_exception.html b/dom/promise/tests/test_promise_uncatchable_exception.html new file mode 100644 index 0000000000..8f7167d566 --- /dev/null +++ b/dom/promise/tests/test_promise_uncatchable_exception.html @@ -0,0 +1,35 @@ + + + + Promise - uncatchable exceptions + + + + +

+ +
+
+
+ + diff --git a/dom/system/nsDeviceSensors.cpp b/dom/system/nsDeviceSensors.cpp index 2665fc4abe..cfb89f0db0 100644 --- a/dom/system/nsDeviceSensors.cpp +++ b/dom/system/nsDeviceSensors.cpp @@ -14,6 +14,7 @@ #include "nsIDOMWindow.h" #include "nsPIDOMWindow.h" #include "nsIDOMDocument.h" +#include "nsIScriptObjectPrincipal.h" #include "nsIServiceManager.h" #include "nsIServiceManager.h" #include "mozilla/Preferences.h" @@ -136,6 +137,37 @@ NS_IMETHODIMP nsDeviceSensors::HasWindowListener(uint32_t aType, nsIDOMWindow *a return NS_OK; } +class DeviceSensorTestEvent : public nsRunnable +{ +public: + DeviceSensorTestEvent(nsDeviceSensors* aTarget, + uint32_t aType) + : mTarget(aTarget) + , mType(aType) + { + } + + NS_IMETHOD Run() + { + SensorData sensorData; + sensorData.sensor() = static_cast(mType); + sensorData.timestamp() = PR_Now(); + sensorData.values().AppendElement(0.5f); + sensorData.values().AppendElement(0.5f); + sensorData.values().AppendElement(0.5f); + sensorData.values().AppendElement(0.5f); + sensorData.accuracy() = SENSOR_ACCURACY_UNRELIABLE; + mTarget->Notify(sensorData); + return NS_OK; + } + +private: + RefPtr mTarget; + uint32_t mType; +}; + +static bool sTestSensorEvents = false; + NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow) { if (!mEnabled) @@ -149,6 +181,20 @@ NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *a } mWindowListeners[aType]->AppendElement(aWindow); + + static bool sPrefCacheInitialized = false; + if (!sPrefCacheInitialized) { + sPrefCacheInitialized = true; + Preferences::AddBoolVarCache(&sTestSensorEvents, + "device.sensors.test.events", + false); + } + + if (sTestSensorEvents) { + nsCOMPtr event = new DeviceSensorTestEvent(this, aType); + NS_DispatchToCurrentThread(event); + } + return NS_OK; } @@ -183,10 +229,29 @@ WindowCannotReceiveSensorEvent (nsPIDOMWindow* aWindow) return true; } - if (aWindow->GetOuterWindow()->IsBackground()) { + bool disabled = aWindow->GetOuterWindow()->IsBackground() || + !aWindow->IsTopLevelWindowActive(); + if (!disabled) { + nsCOMPtr top = aWindow->GetScriptableTop(); + nsCOMPtr sop = do_QueryInterface(aWindow); + nsCOMPtr topSop = do_QueryInterface(top); + if (!sop || !topSop) { + return true; + } + + nsIPrincipal* principal = sop->GetPrincipal(); + nsIPrincipal* topPrincipal = topSop->GetPrincipal(); + if (!principal || !topPrincipal) { + return true; + } + + disabled = !principal->Subsumes(topPrincipal); + } + + if (disabled) { nsCOMPtr permMgr = services::GetPermissionManager(); - NS_ENSURE_TRUE(permMgr, false); + NS_ENSURE_TRUE(permMgr, true); uint32_t permission = nsIPermissionManager::DENY_ACTION; permMgr->TestPermissionFromWindow(aWindow, "background-sensors", &permission); return permission != nsIPermissionManager::ALLOW_ACTION; @@ -431,7 +496,11 @@ nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc, double z) { // Attempt to coalesce events - bool fireEvent = TimeStamp::Now() > mLastDOMMotionEventTime + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL); + TimeDuration sensorPollDuration = + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL); + bool fireEvent = + (TimeStamp::Now() > mLastDOMMotionEventTime + sensorPollDuration) || + sTestSensorEvents; switch (type) { case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION: diff --git a/dom/system/tests/file_bug1197901.html b/dom/system/tests/file_bug1197901.html new file mode 100644 index 0000000000..44e63c3c16 --- /dev/null +++ b/dom/system/tests/file_bug1197901.html @@ -0,0 +1,16 @@ +
Sensor events testing
+ diff --git a/dom/system/tests/mochitest.ini b/dom/system/tests/mochitest.ini index 1d7d2df270..d53ab12fde 100644 --- a/dom/system/tests/mochitest.ini +++ b/dom/system/tests/mochitest.ini @@ -1,6 +1,9 @@ [DEFAULT] -skip-if = buildapp != 'b2g' support-files = preload-SystemUpdateManager-jsm.js + file_bug1197901.html [test_system_update_enabled.html] +skip-if = buildapp != 'b2g' +[test_bug1197901.html] +skip-if = buildapp == 'mulet' diff --git a/dom/system/tests/test_bug1197901.html b/dom/system/tests/test_bug1197901.html new file mode 100644 index 0000000000..938943b66d --- /dev/null +++ b/dom/system/tests/test_bug1197901.html @@ -0,0 +1,96 @@ + + + + + + Test for Bug 1197901 + + + + + +Mozilla Bug +

+ +
+
+ + + + diff --git a/dom/webidl/TestFunctions.webidl b/dom/webidl/TestFunctions.webidl new file mode 100644 index 0000000000..2701ba8e04 --- /dev/null +++ b/dom/webidl/TestFunctions.webidl @@ -0,0 +1,12 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// A dumping ground for random testing functions +[Pref="dom.expose_test_interfaces"] +interface TestFunctions { + [Throws] + static void throwUncatchableException(); +}; diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl index 922e688447..49fabef01b 100644 --- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -93,10 +93,10 @@ Window implements WindowEventHandlers; interface WindowTimers { [Throws] long setTimeout(Function handler, optional long timeout = 0, any... arguments); [Throws] long setTimeout(DOMString handler, optional long timeout = 0, any... unused); - [Throws] void clearTimeout(optional long handle = 0); + void clearTimeout(optional long handle = 0); [Throws] long setInterval(Function handler, optional long timeout, any... arguments); [Throws] long setInterval(DOMString handler, optional long timeout, any... unused); - [Throws] void clearInterval(optional long handle = 0); + void clearInterval(optional long handle = 0); }; Window implements WindowTimers; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 478abb7d56..48fd3fd97a 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -687,7 +687,8 @@ WEBIDL_FILES += [ # We only expose our prefable test interfaces in debug builds, just to be on # the safe side. if CONFIG['MOZ_DEBUG']: - WEBIDL_FILES += ['TestInterfaceJS.webidl', + WEBIDL_FILES += ['TestFunctions.webidl', + 'TestInterfaceJS.webidl', 'TestInterfaceJSDictionaries.webidl', 'TestInterfaceJSMaplikeSetlikeIterable.webidl'] diff --git a/dom/workers/ServiceWorker.h b/dom/workers/ServiceWorker.h index 15fc4fbc7a..6a6ec74c94 100644 --- a/dom/workers/ServiceWorker.h +++ b/dom/workers/ServiceWorker.h @@ -27,7 +27,7 @@ ServiceWorkerVisible(JSContext* aCx, JSObject* aObj); class ServiceWorker final : public DOMEventTargetHelper { - friend class ServiceWorkerManager; + friend class ServiceWorkerInfo; public: NS_DECL_ISUPPORTS_INHERITED @@ -68,7 +68,7 @@ public: ErrorResult& aRv); private: - // This class can only be created from the ServiceWorkerManager. + // This class can only be created from ServiceWorkerInfo::GetOrCreateInstance(). ServiceWorker(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo); // This class is reference-counted and will be destroyed from Release(). diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 0ad9475d5b..8d26dfe97a 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -2813,7 +2813,8 @@ ServiceWorkerManager::HandleError(JSContext* aCx, void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) { - if (mPendingUninstall || !mActiveWorker) { + if (mPendingUninstall || !mActiveWorker || + mActiveWorker->State() != ServiceWorkerState::Activating) { return; } @@ -3508,7 +3509,7 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow, return NS_ERROR_DOM_NOT_FOUND_ERR; } - RefPtr serviceWorker = new ServiceWorker(window, info); + RefPtr serviceWorker = info->GetOrCreateInstance(window); serviceWorker->SetState(info->State()); serviceWorker.forget(aServiceWorker); @@ -3737,7 +3738,7 @@ ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, MOZ_ASSERT(registration->mActiveWorker); RefPtr serviceWorker = - new ServiceWorker(window, registration->mActiveWorker); + registration->mActiveWorker->GetOrCreateInstance(window); serviceWorker.forget(aServiceWorker); return NS_OK; @@ -5203,4 +5204,27 @@ ServiceWorkerInfo::GetNextID() const return ++gServiceWorkerInfoCurrentID; } +already_AddRefed +ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindow* aWindow) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); + + RefPtr ref; + + for (uint32_t i = 0; i < mInstances.Length(); ++i) { + MOZ_ASSERT(mInstances[i]); + if (mInstances[i]->GetOwner() == aWindow) { + ref = mInstances[i]; + break; + } + } + + if (!ref) { + ref = new ServiceWorker(aWindow, this); + } + + return ref.forget(); +} + END_WORKERS_NAMESPACE diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 115c1ac82c..e91bd43870 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -305,6 +305,9 @@ public: void RemoveWorker(ServiceWorker* aWorker); + + already_AddRefed + GetOrCreateInstance(nsPIDOMWindow* aWindow); }; #define NS_SERVICEWORKERMANAGER_IMPL_IID \ diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index 40f888133d..23d0374d2d 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -22,6 +22,7 @@ #include "nsIServiceWorkerManager.h" #include "nsISupportsPrimitives.h" #include "nsPIDOMWindow.h" +#include "nsContentUtils.h" #include "WorkerPrivate.h" #include "Workers.h" @@ -768,21 +769,10 @@ ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv) return nullptr; } - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(globalObject))) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - JSContext* cx = jsapi.cx(); - - JS::RootedObject globalJs(cx, globalObject->GetGlobalJSObject()); - GlobalObject global(cx, globalJs); - // TODO: bug 1148117. This will fail when swr is exposed on workers - JS::Rooted jsImplObj(cx); - nsCOMPtr unused = ConstructJSImplementation(cx, "@mozilla.org/push/PushManager;1", - global, &jsImplObj, aRv); + JS::Rooted jsImplObj(nsContentUtils::RootingCxForThread()); + ConstructJSImplementation("@mozilla.org/push/PushManager;1", + globalObject, &jsImplObj, aRv); if (aRv.Failed()) { return nullptr; } diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index c773afcfcc..8c98f3cc81 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -246,7 +246,7 @@ WorkerGlobalScope::SetTimeout(JSContext* /* unused */, } void -WorkerGlobalScope::ClearTimeout(int32_t aHandle, ErrorResult& aRv) +WorkerGlobalScope::ClearTimeout(int32_t aHandle) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->ClearTimeout(aHandle); @@ -287,7 +287,7 @@ WorkerGlobalScope::SetInterval(JSContext* /* unused */, } void -WorkerGlobalScope::ClearInterval(int32_t aHandle, ErrorResult& aRv) +WorkerGlobalScope::ClearInterval(int32_t aHandle) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->ClearTimeout(aHandle); diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index e6919c54c1..50de58e397 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -115,7 +115,7 @@ public: const int32_t aTimeout, const Sequence& /* unused */, ErrorResult& aRv); void - ClearTimeout(int32_t aHandle, ErrorResult& aRv); + ClearTimeout(int32_t aHandle); int32_t SetInterval(JSContext* aCx, Function& aHandler, const Optional& aTimeout, @@ -125,7 +125,7 @@ public: const Optional& aTimeout, const Sequence& /* unused */, ErrorResult& aRv); void - ClearInterval(int32_t aHandle, ErrorResult& aRv); + ClearInterval(int32_t aHandle); void Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const; diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index b90f6d5704..81202f3a09 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -13,6 +13,8 @@ #include "nsIXPConnect.h" #include "jsfriendapi.h" +#include "js/TracingAPI.h" +#include "js/GCPolicyAPI.h" #include "mozilla/ArrayUtils.h" #include "mozilla/dom/Exceptions.h" #include "mozilla/dom/File.h" @@ -31,11 +33,19 @@ #include "WorkerRunnable.h" #include "XMLHttpRequestUpload.h" +#include "mozilla/UniquePtr.h" + using namespace mozilla; using namespace mozilla::dom; USING_WORKERS_NAMESPACE +/* static */ void +XMLHttpRequest::StateData::trace(JSTracer *aTrc) +{ + JS::TraceEdge(aTrc, &mResponse, "XMLHttpRequest::StateData::mResponse"); +} + /** * XMLHttpRequest in workers * @@ -547,27 +557,6 @@ class EventRunnable final : public MainThreadProxyRunnable JS::PersistentRooted mScopeObj; public: - class MOZ_RAII StateDataAutoRooter : private JS::CustomAutoRooter - { - XMLHttpRequest::StateData* mStateData; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - explicit StateDataAutoRooter(JSContext* aCx, XMLHttpRequest::StateData* aData - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : CustomAutoRooter(aCx), mStateData(aData) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - private: - virtual void trace(JSTracer* aTrc) - { - JS::TraceEdge(aTrc, &mStateData->mResponse, - "XMLHttpRequest::StateData::mResponse"); - } - }; - EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType, bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal, JS::Handle aScopeObj) @@ -1336,8 +1325,7 @@ EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) } } - nsAutoPtr state(new XMLHttpRequest::StateData()); - StateDataAutoRooter rooter(aCx, state); + JS::Rooted> state(aCx, new XMLHttpRequest::StateData()); state->mResponseTextResult = mResponseTextResult; state->mResponseText = mResponseText; @@ -1385,7 +1373,7 @@ EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) state->mResponseURL = mResponseURL; XMLHttpRequest* xhr = mProxy->mXMLHttpRequestPrivate; - xhr->UpdateState(*state, mUseCachedArrayBufferResponse); + xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse); if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) { return true; diff --git a/dom/workers/XMLHttpRequest.h b/dom/workers/XMLHttpRequest.h index 452ec1cb8a..23cd19e267 100644 --- a/dom/workers/XMLHttpRequest.h +++ b/dom/workers/XMLHttpRequest.h @@ -50,6 +50,8 @@ public: mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK) { } + + void trace(JSTracer* trc); }; private: diff --git a/dom/workers/test/serviceworkers/test_claim_oninstall.html b/dom/workers/test/serviceworkers/test_claim_oninstall.html index 2779231bad..4605cfb76f 100644 --- a/dom/workers/test/serviceworkers/test_claim_oninstall.html +++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html @@ -37,19 +37,19 @@ } var p = new Promise(function(res, rej) { - registration.installing.onstatechange = function(e) { - ok(registration.waiting, "Worker should be in waitinging state"); - - // The worker will become active only if claim will reject inside the - // install handler. - registration.waiting.onstatechange = function(e) { - ok(registration.active, "Claim should reject if the worker is not active"); + var worker = registration.installing; + worker.onstatechange = function(e) { + if (worker.state === 'installed') { + is(worker, registration.waiting, "Worker should be in waiting state"); + } else if (worker.state === 'activated') { + // The worker will become active only if claim will reject inside the + // install handler. + is(worker, registration.active, + "Claim should reject if the worker is not active"); ok(navigator.serviceWorker.controller === null, "Client is not controlled."); e.target.onstatechange = null; res(); } - - e.target.onstatechange = null; } }); diff --git a/js/public/Conversions.h b/js/public/Conversions.h index ac09cc2261..8dc16da9b0 100644 --- a/js/public/Conversions.h +++ b/js/public/Conversions.h @@ -556,6 +556,12 @@ ToInt16(double d) return detail::ToIntWidth(d); } +inline uint16_t +ToUint16(double d) +{ + return detail::ToUintWidth(d); +} + /* WEBIDL 4.2.10 */ inline int64_t ToInt64(double d) diff --git a/js/public/GCPolicyAPI.h b/js/public/GCPolicyAPI.h index 138aa1d767..fa531fa286 100644 --- a/js/public/GCPolicyAPI.h +++ b/js/public/GCPolicyAPI.h @@ -35,6 +35,7 @@ #include "js/TraceKind.h" #include "js/TracingAPI.h" +#include "mozilla/UniquePtr.h" class JSAtom; class JSFunction; @@ -111,6 +112,19 @@ struct GCPolicy> } }; +// GCPolicy> forwards the contained pointer to GCPolicy. +template +struct GCPolicy> +{ + static mozilla::UniquePtr initial() { return mozilla::UniquePtr(); } + static void trace(JSTracer* trc, mozilla::UniquePtr* tp, const char* name) { + GCPolicy::trace(trc, tp->get(), name); + } + static bool needsSweep(mozilla::UniquePtr* tp) { + return GCPolicy::needsSweep(tp->get()); + } +}; + } // namespace js #endif // GCPolicyAPI_h diff --git a/js/public/TraceKind.h b/js/public/TraceKind.h index 4a5f809f82..3a2cce9c5c 100644 --- a/js/public/TraceKind.h +++ b/js/public/TraceKind.h @@ -7,6 +7,8 @@ #ifndef js_TraceKind_h #define js_TraceKind_h +#include "mozilla/UniquePtr.h" + #include "js/TypeDecls.h" // Forward declarations of all the types a TraceKind can denote. @@ -134,9 +136,13 @@ struct MapTypeToRootKind { }; template struct MapTypeToRootKind { - static const JS::RootKind kind = \ + static const JS::RootKind kind = JS::MapTraceKindToRootKind::kind>::kind; }; +template +struct MapTypeToRootKind> { + static const JS::RootKind kind = JS::MapTypeToRootKind::kind; +}; template <> struct MapTypeToRootKind { static const JS::RootKind kind = JS::RootKind::Value; }; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index aa6ab6bca9..1744c0c971 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -729,7 +729,6 @@ template Parser::~Parser() { MOZ_ASSERT(checkOptionsCalled); - alloc.release(tempPoolMark); /* @@ -3941,6 +3940,74 @@ Parser::AutoPushStmtInfoPC::makeInnermostLexicalScope(StaticBlockS return generateBlockId(); } +template +Parser::PossibleError::PossibleError(Parser& parser) + : parser_(parser) +{ + state_ = ErrorState::None; +} + +template +bool +Parser::PossibleError::setPending(ParseReportKind kind, unsigned errorNumber, + bool strict) +{ + if (hasError()) + return false; + + // If we report an error later, we'll do it from the position where we set + // the state to pending. + offset_ = parser_.pos().begin; + reportKind_ = kind; + strict_ = strict; + errorNumber_ = errorNumber; + state_ = ErrorState::Pending; + + return true; +} + +template +void +Parser::PossibleError::setResolved() +{ + state_ = ErrorState::None; +} + +template +bool +Parser::PossibleError::hasError() +{ + return state_ == ErrorState::Pending; +} + +template +bool +Parser::PossibleError::checkForExprErrors() +{ + bool err = hasError(); + if (err) + parser_.reportWithOffset(reportKind_, strict_, offset_, errorNumber_); + return !err; +} + +template +void +Parser::PossibleError::transferErrorTo(PossibleError* other) +{ + if (other) { + MOZ_ASSERT(this != other); + MOZ_ASSERT(!other->hasError()); + // We should never allow fields to be copied between instances + // that point to different underlying parsers. + MOZ_ASSERT(&parser_ == &other->parser_); + other->offset_ = offset_; + other->reportKind_ = reportKind_; + other->errorNumber_ = errorNumber_; + other->strict_ = strict_; + other->state_ = state_; + } +} + template static inline bool HasOuterLexicalBinding(ParseContext* pc, StmtInfoPC* stmt, HandleAtom atom) @@ -4433,7 +4500,8 @@ Parser::destructuringExpr(YieldHandling yieldHandling, BindDatainDeclDestructuring = true; - Node pn = primaryExpr(yieldHandling, TripledotProhibited, tt); + Node pn = primaryExpr(yieldHandling, TripledotProhibited, + nullptr /* possibleError */, tt); pc->inDeclDestructuring = false; if (!pn) return null(); @@ -4586,10 +4654,10 @@ Parser::expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling) { MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF); - - return forHeadKind == PNK_FOROF + Node pn = forHeadKind == PNK_FOROF ? assignExpr(InAllowed, yieldHandling, TripledotProhibited) : expr(InAllowed, yieldHandling, TripledotProhibited); + return pn; } template @@ -4605,7 +4673,11 @@ Parser::declarationPattern(Node decl, TokenKind tt, BindDatainDeclDestructuring = true; - pattern = primaryExpr(yieldHandling, TripledotProhibited, tt); + + // No possible error is required because we already know we're + // destructuring. + pattern = primaryExpr(yieldHandling, TripledotProhibited, + nullptr /* possibleError */ , tt); pc->inDeclDestructuring = false; } if (!pattern) @@ -4652,7 +4724,14 @@ Parser::declarationPattern(Node decl, TokenKind tt, BindData::tryStatement(YieldHandling yieldHandling) if (!catchName) return null(); break; - case TOK_YIELD: if (yieldHandling == YieldIsKeyword) { report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); @@ -7462,9 +7540,12 @@ Parser::statement(YieldHandling yieldHandling, bool canHaveDirecti template typename ParseHandler::Node Parser::expr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { - Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, invoked); + Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, + possibleError, invoked); if (!pn) return null(); @@ -7478,9 +7559,26 @@ Parser::expr(InHandling inHandling, YieldHandling yieldHandling, if (!seq) return null(); while (true) { - pn = assignExpr(inHandling, yieldHandling, tripledotHandling); + // Additional calls to assignExpr should not reuse the possibleError + // which had been passed into the function. Otherwise we would lose + // information needed to determine whether or not we're dealing with + // a non-recoverable situation. + PossibleError possibleErrorInner(*this); + pn = assignExpr(inHandling, yieldHandling, tripledotHandling, + &possibleErrorInner); if (!pn) return null(); + + // If we find an error here we should report it immedately instead of + // passing it back out of the function. + if (possibleErrorInner.hasError()) { + + // We begin by checking for an outer pending error since it would + // have occurred first. + if (possibleError->checkForExprErrors()) + possibleErrorInner.checkForExprErrors(); + return null(); + } handler.addList(seq, pn); if (!tokenStream.matchToken(&matched, TOK_COMMA)) @@ -7491,6 +7589,19 @@ Parser::expr(InHandling inHandling, YieldHandling yieldHandling, return seq; } +template +typename ParseHandler::Node +Parser::expr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + InvokedPrediction invoked) +{ + PossibleError possibleError(*this); + Node pn = expr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked); + if (!pn || !possibleError.checkForExprErrors()) + return null(); + return pn; +} + static const JSOp ParseNodeKindToJSOp[] = { JSOP_OR, JSOP_AND, @@ -7578,7 +7689,9 @@ Precedence(ParseNodeKind pnk) { template MOZ_ALWAYS_INLINE typename ParseHandler::Node Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { // Shift-reduce parser for the binary operator part of the JS expression // syntax. @@ -7591,7 +7704,7 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling Node pn; for (;;) { - pn = unaryExpr(yieldHandling, tripledotHandling, invoked); + pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); if (!pn) return pn; @@ -7641,19 +7754,22 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling template MOZ_ALWAYS_INLINE typename ParseHandler::Node Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { - Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, invoked); + Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked); if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) return condition; - - Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); if (!thenExpr) return null(); MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); - Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited); + Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited, + possibleError); if (!elseExpr) return null(); @@ -7666,7 +7782,8 @@ Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandli template bool -Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor) +Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor, + PossibleError* possibleError) { MOZ_ASSERT(flavor != KeyedDestructuringAssignment, "destructuring must use special checking/marking code, not " @@ -7678,7 +7795,13 @@ Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor return false; } - return checkDestructuringPattern(nullptr, target); + bool isDestructuring = checkDestructuringPattern(nullptr, target); + // Here we've successfully distinguished between destructuring and + // an object literal. In the case where "CoverInitializedName" + // syntax was used there will be a pending error that needs clearing. + if (possibleError && isDestructuring) + possibleError->setResolved(); + return isDestructuring; } // All other permitted targets are simple. @@ -7706,7 +7829,9 @@ Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor template typename ParseHandler::Node Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { JS_CHECK_RECURSION(context, return null()); @@ -7758,9 +7883,11 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl TokenStream::Position start(keepAtoms); tokenStream.tell(&start); - Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, invoked); - if (!lhs) + PossibleError possibleErrorInner(*this); + Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked); + if (!lhs) { return null(); + } ParseNodeKind kind; JSOp op; @@ -7780,6 +7907,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl case TOK_POWASSIGN: kind = PNK_POWASSIGN; op = JSOP_POW; break; case TOK_ARROW: { + // A line terminator between ArrowParameters and the => should trigger a SyntaxError. tokenStream.ungetToken(); TokenKind next = TOK_EOF; @@ -7842,24 +7970,42 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl default: MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment()); + possibleErrorInner.transferErrorTo(possibleError); tokenStream.ungetToken(); return lhs; } AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment; - if (!checkAndMarkAsAssignmentLhs(lhs, flavor)) + if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner)) + return null(); + if (!possibleErrorInner.checkForExprErrors()) return null(); bool saved = pc->inDeclDestructuring; pc->inDeclDestructuring = false; - Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); + Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited, + possibleError); pc->inDeclDestructuring = saved; if (!rhs) return null(); - return handler.newAssignment(kind, lhs, rhs, pc, op); } +template +typename ParseHandler::Node +Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + InvokedPrediction invoked) +{ + PossibleError possibleError(*this); + Node expr = assignExpr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked); + if (!expr || !possibleError.checkForExprErrors()) + return null(); + + return expr; +} + + template bool Parser::isValidSimpleAssignmentTarget(Node node, @@ -7984,8 +8130,9 @@ typename ParseHandler::Node Parser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin) { - Node kid = unaryExpr(yieldHandling, TripledotProhibited); - if (!kid) + PossibleError possibleError(*this); + Node kid = unaryExpr(yieldHandling, TripledotProhibited, &possibleError); + if (!kid || !possibleError.checkForExprErrors()) return null(); return handler.newUnary(kind, op, begin, kid); } @@ -7993,7 +8140,7 @@ Parser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kin template typename ParseHandler::Node Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, - InvokedPrediction invoked) + PossibleError* possibleError, InvokedPrediction invoked) { JS_CHECK_RECURSION(context, return null()); @@ -8025,7 +8172,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t // // Evaluates expression, triggering a runtime ReferenceError for // // the undefined name. // typeof (1, nonExistentName); - Node kid = unaryExpr(yieldHandling, TripledotProhibited); + Node kid = unaryExpr(yieldHandling, TripledotProhibited, possibleError); if (!kid) return null(); @@ -8038,7 +8185,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::Operand)) return null(); - Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2, true); + Node pn2 = memberExpr(yieldHandling, TripledotProhibited, possibleError, tt2, true); if (!pn2) return null(); AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; @@ -8051,7 +8198,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } case TOK_DELETE: { - Node expr = unaryExpr(yieldHandling, TripledotProhibited); + Node expr = unaryExpr(yieldHandling, TripledotProhibited, possibleError); if (!expr) return null(); @@ -8067,7 +8214,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } default: { - Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, + Node pn = memberExpr(yieldHandling, tripledotHandling, possibleError, tt, /* allowCallSyntax = */ true, invoked); if (!pn) return null(); @@ -8490,7 +8637,8 @@ Parser::checkAndMarkSuperScope() template typename ParseHandler::Node Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, - TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked) + PossibleError* possibleError, TokenKind tt, + bool allowCallSyntax, InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); @@ -8514,7 +8662,8 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling // Gotten by tryNewTarget tt = tokenStream.currentToken().type; - Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, false, PredictInvoked); + Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, + possibleError, tt, false, PredictInvoked); if (!ctorExpr) return null(); @@ -8539,7 +8688,7 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling if (!lhs) return null(); } else { - lhs = primaryExpr(yieldHandling, tripledotHandling, tt, invoked); + lhs = primaryExpr(yieldHandling, tripledotHandling, possibleError, tt, invoked); if (!lhs) return null(); } @@ -8570,7 +8719,7 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling return null(); } } else if (tt == TOK_LB) { - Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited); + Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!propExpr) return null(); @@ -8693,6 +8842,19 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling return lhs; } +template +typename ParseHandler::Node +Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, + bool allowCallSyntax, InvokedPrediction invoked) +{ + PossibleError possibleError(*this); + Node pn = memberExpr(yieldHandling, tripledotHandling, &possibleError, tt, + allowCallSyntax, invoked); + if (!pn || !possibleError.checkForExprErrors()) + return null(); + return pn; +} + template typename ParseHandler::Node Parser::newName(PropertyName* name) @@ -9005,13 +9167,15 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return propName; } - if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) { + if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) { if (isGenerator) { report(ParseError, false, null(), JSMSG_BAD_PROP_ID); return null(); } tokenStream.ungetToken(); - *propType = PropertyType::Shorthand; + *propType = tt == TOK_ASSIGN ? + PropertyType::CoverInitializedName : + PropertyType::Shorthand; return propName; } @@ -9052,7 +9216,7 @@ Parser::computedPropertyName(YieldHandling yieldHandling, Node lit template typename ParseHandler::Node -Parser::objectLiteral(YieldHandling yieldHandling) +Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); @@ -9061,6 +9225,7 @@ Parser::objectLiteral(YieldHandling yieldHandling) return null(); bool seenPrototypeMutation = false; + bool seenCoverInitializedName = false; RootedAtom propAtom(context); for (;;) { TokenKind tt; @@ -9119,6 +9284,48 @@ Parser::objectLiteral(YieldHandling yieldHandling) if (!handler.addShorthand(literal, propName, nameExpr)) return null(); + } else if (propType == PropertyType::CoverInitializedName) { + /* + * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand + * with default values, as per ES6 12.14.5 (2016/2/4) + */ + if (!tokenStream.checkForKeyword(propAtom, nullptr)) + return null(); + + Node lhs = identifierName(yieldHandling); + if (!lhs) + return null(); + + tokenStream.consumeKnownToken(TOK_ASSIGN); + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); + + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP); + if (!propExpr) + return null(); + + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + + if (!abortIfSyntaxParser()) + return null(); + + if (!seenCoverInitializedName) { + seenCoverInitializedName = true; + // "shorthand default" or "CoverInitializedName" syntax is only + // valid in the case of destructuring. Here we set a pending error so + // that later in the parse, once we've determined whether or not we're + // destructuring, the error can be reported or ignored appropriately. + if (possibleError && + !possibleError->setPending(ParseError, JSMSG_BAD_PROP_ID, false)) { + + // Report any previously pending error. + possibleError->checkForExprErrors(); + return null(); + } + } + } else { // FIXME: Implement ES6 function "name" property semantics // (bug 883377). @@ -9218,7 +9425,8 @@ Parser::tryNewTarget(Node &newTarget) template typename ParseHandler::Node Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, - TokenKind tt, InvokedPrediction invoked) + PossibleError* possibleError, TokenKind tt, + InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); JS_CHECK_RECURSION(context, return null()); @@ -9234,7 +9442,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling return arrayInitializer(yieldHandling); case TOK_LC: - return objectLiteral(yieldHandling); + return objectLiteral(yieldHandling, possibleError); case TOK_LP: { TokenKind next; @@ -9266,7 +9474,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling return generatorComprehension(begin); } - Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed); + Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError); if (!expr) return null(); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); @@ -9370,6 +9578,16 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling } } +template +typename ParseHandler::Node +Parser::exprInParens(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); + return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked); +} + template typename ParseHandler::Node Parser::exprInParens(InHandling inHandling, YieldHandling yieldHandling, diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 513c05fe36..e56386ddda 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -374,6 +374,7 @@ enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody }; enum class PropertyType { Normal, Shorthand, + CoverInitializedName, Getter, GetterNoExpressionClosure, Setter, @@ -415,6 +416,73 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter operator StmtInfoPC*() { return &stmt_; } }; + /* + * A class for temporarily stashing errors while parsing continues. + * + * The ability to stash an error is useful for handling situations where we + * aren't able to verify that an error has occurred until later in the parse. + * For instance | ({x=1}) | is always parsed as an object literal with + * a SyntaxError, however, in the case where it is followed by '=>' we rewind + * and reparse it as a valid arrow function. Here a PossibleError would be + * set to 'pending' when the initial SyntaxError was encountered then 'resolved' + * just before rewinding the parser. + * + * When using PossibleError one should set a pending error at the location + * where an error occurs. From that point, the error may be resolved + * (invalidated) or left until the PossibleError is checked. + * + * Ex: + * PossibleError possibleError(*this); + * possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false); + * // A JSMSG_BAD_PROP_ID ParseError is reported, returns false. + * possibleError.checkForExprErrors(); + * + * PossibleError possibleError(*this); + * possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false); + * possibleError.setResolved(); + * // Returns true, no error is reported. + * possibleError.checkForExprErrors(); + * + * PossibleError possibleError(*this); + * // Returns true, no error is reported. + * possibleError.checkForExprErrors(); + */ + class MOZ_STACK_CLASS PossibleError + { + enum ErrorState { None, Pending }; + ErrorState state_; + + // Error reporting fields. + uint32_t offset_; + unsigned errorNumber_; + ParseReportKind reportKind_; + Parser& parser_; + bool strict_; + + public: + explicit PossibleError(Parser& parser); + + // Set a pending error. Only a single error may be set per instance. + // Returns true on success or false on failure. + bool setPending(ParseReportKind kind, unsigned errorNumber, bool strict); + + // Resolve any pending error. + void setResolved(); + + // Return true if an error is pending without reporting + bool hasError(); + + // If there is a pending error report it and return false, otherwise return + // true. + bool checkForExprErrors(); + + // Pass pending errors between possible error instances. This is useful + // for extending the lifetime of a pending error beyond the scope of + // the PossibleError where it was initially set (keeping in mind that + // PossibleError is a MOZ_STACK_CLASS). + void transferErrorTo(PossibleError* other); + }; + public: ExclusiveContext* const context; LifoAlloc& alloc; @@ -765,7 +833,15 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node expr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked = PredictUninvoked); + Node expr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + InvokedPrediction invoked = PredictUninvoked); + Node assignExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked = PredictUninvoked); Node assignExpr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, InvokedPrediction invoked = PredictUninvoked); @@ -773,16 +849,26 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node yieldExpression(InHandling inHandling); Node condExpr1(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked = PredictUninvoked); Node orExpr1(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, - InvokedPrediction invoked = PredictUninvoked); + PossibleError* possibleError, + InvokedPrediction invoked = PredictUninvoked); Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked = PredictUninvoked); + Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, TokenKind tt, + bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked); Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked); - Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, + Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, TokenKind tt, InvokedPrediction invoked = PredictUninvoked); + Node exprInParens(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError); Node exprInParens(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling); @@ -854,7 +940,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter ForInOrOfTarget }; - bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor); + bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor, + PossibleError* possibleError=nullptr); bool matchInOrOf(bool* isForInp, bool* isForOfp); bool checkFunctionArguments(); @@ -909,7 +996,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node arrayInitializer(YieldHandling yieldHandling); Node newRegExp(); - Node objectLiteral(YieldHandling yieldHandling); + Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError); enum PrepareLexicalKind { PrepareLet, diff --git a/js/src/jit-test/tests/basic/destructuring-default.js b/js/src/jit-test/tests/basic/destructuring-default.js index 545a1f186d..02eafcfc50 100644 --- a/js/src/jit-test/tests/basic/destructuring-default.js +++ b/js/src/jit-test/tests/basic/destructuring-default.js @@ -4,6 +4,7 @@ load(libdir + 'eqArrayHelper.js'); var arrayPattern = '[a = 1, b = 2, c = 3, d = 4, e = 5, f = 6]'; var objectPattern = '{0: a = 1, 1: b = 2, 2: c = 3, 3: d = 4, 4: e = 5, 5: f = 6}'; +var objectPatternShorthand = '{a = 1, b = 2, c = 3, d = 4, e = 5, f = 6}'; var nestedPattern = '{a: a = 1, b: [b = 2] = [], c: {c: [c]} = {c: [3]}, d: {d, e} = {d: 4, e: 5}, f: f = 6}'; function testAll(fn) { @@ -17,6 +18,11 @@ function testAll(fn) { assertEqArray(fn(objectPattern, [undefined, 0, false, null, "", undefined]), [1, 0, false, null, "", 6]); assertEqArray(fn(objectPattern, [0, false]), [0, false, 3, 4, 5, 6]); + assertEqArray(fn(objectPatternShorthand, {}), [1, 2, 3, 4, 5, 6]); + assertEqArray(fn(objectPatternShorthand, {a: 2, b: 3, c: 4, d: 5, e: 6, f: 7, g: 8, h: 9}), [2, 3, 4, 5, 6, 7]); + assertEqArray(fn(objectPatternShorthand, {a: undefined, b: 0, c: false, d: null, e: "", f: undefined}), + [1, 0, false, null, "", 6]); + assertEqArray(fn(objectPatternShorthand, {a: 0, b: false}), [0, false, 3, 4, 5, 6]); assertEqArray(fn(nestedPattern, {}), [1, 2, 3, 4, 5, 6]); assertEqArray(fn(nestedPattern, {a: 2, b: [], c: undefined}), [2, 2, 3, 4, 5, 6]); assertEqArray(fn(nestedPattern, {a: undefined, b: [3], c: {c: [4]}}), [1, 3, 4, 4, 5, 6]); @@ -175,5 +181,9 @@ if (defaultsSupportedInForVar) { b = undefined; for (let {1: c = 10} in " ") { b = c; } assertEq(b, 10); + + b = undefined; + for (let {c = 10} in " ") { b = c; } + assertEq(b, 10); `)(); } diff --git a/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js b/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js new file mode 100644 index 0000000000..c913753511 --- /dev/null +++ b/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js @@ -0,0 +1,103 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Ensure that the syntax used in shorthand destructuring with defaults +// e.g. |{x=1, y=2} = {}| properly raises a syntax error within an object +// literal. As per ES6 12.2.6 Object Initializer, "NOTE 3." + +const SYNTAX_ERROR_STMTS = [ + // expressions + "({x={}={}}),", + "({y={x={}={}={}={}={}={}={}={}}={}}),", + "({a=1, b=2, c=3, x=({}={})}),", + "({x=1, y={z={1}}})", + "({x=1} = {y=1});", + "({x: y={z=1}}={})", + "({x=1}),", + "({z=1}={}, {w=2}, {e=3})=>{};", + "({z={x=1}})=>{};", + "({x = ({y=1}) => y})", + "(({x=1})) => x", + "({e=[]}==(;", + // declarations + "let o = {x=1};", + "var j = {x=1};", + "var j = {x={y=1}}={};", + "const z = {x=1};", + "const z = {x={y=1}}={};", + "const {x=1};", + "const {x={y=33}}={};", + "var {x=1};", + "let {x=1};", + "let x, y, {z=1}={}, {w=2}, {e=3};", + // array initialization + "[{x=1, y = ({z=2} = {})}];", + // try/catch + "try {throw 'a';} catch ({x={y=1}}) {}", + // if/else + "if ({k: 1, x={y=2}={}}) {}", + "if (false) {} else if (true) { ({x=1}) }", + // switch + "switch ('c') { case 'c': ({x=1}); }", + // for + "for ({x=1}; 1;) {1}", + "for ({x={y=2}}; 1;) {1}", + "for (var x = 0; x < 2; x++) { ({x=1, y=2}) }", + "for (let x=1;{x=1};){}", + "for (let x=1;{x={y=2}};){}", + "for (let x=1;1;{x=1}){}", + "for (let x=1;1;{x={y=2}}){}", + // while + "while ({x=1}) {1};", + "while ({x={y=2}}={}) {1};", + // with + "with ({x=1}) {};", + "with ({x={y=3}={}}) {};", + "with (Math) { ({x=1}) };" +] + +for (var stmt of SYNTAX_ERROR_STMTS) { + assertThrowsInstanceOf(() => { + eval(stmt); + }, SyntaxError); +} + +const REFERENCE_ERROR_STMTS = [ + "({x} += {});", + "({x = 1}) = {x: 2};", +] + +for (var stmt of REFERENCE_ERROR_STMTS) { + assertThrowsInstanceOf(() => { + eval(stmt); + }, ReferenceError); +} + +// A few tricky but acceptable cases: +// see https://bugzilla.mozilla.org/show_bug.cgi?id=932080#c2 + +assertEq((({a = 0}) => a)({}), 0); +assertEq((({a = 0} = {}) => a)({}), 0); +assertEq((({a = 0} = {}) => a)({a: 1}), 1); + +{ + let x, y; + ({x=1} = {}); + assertEq(x, 1); + ({x=1} = {x: 4}); + assertEq(x, 4); + ({x=1, y=2} = {}) + assertEq(x, 1); + assertEq(y, 2); +} + +{ + let {x={i=1, j=2}={}}={}; + assertDeepEq(x, ({})); + assertEq(i, 1); + assertEq(j, 2); +} + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index 71db62c678..196e92733c 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -58,6 +58,118 @@ ValueIsLength(const Value& v, uint32_t* len) return false; } +template +inline To +ConvertNumber(From src); + +template<> +inline int8_t +ConvertNumber(float src) +{ + return JS::ToInt8(src); +} + +template<> +inline uint8_t +ConvertNumber(float src) +{ + return JS::ToUint8(src); +} + +template<> +inline uint8_clamped +ConvertNumber(float src) +{ + return uint8_clamped(src); +} + +template<> +inline int16_t +ConvertNumber(float src) +{ + return JS::ToInt16(src); +} + +template<> +inline uint16_t +ConvertNumber(float src) +{ + return JS::ToUint16(src); +} + +template<> +inline int32_t +ConvertNumber(float src) +{ + return JS::ToInt32(src); +} + +template<> +inline uint32_t +ConvertNumber(float src) +{ + return JS::ToUint32(src); +} + +template<> inline int8_t +ConvertNumber(double src) +{ + return JS::ToInt8(src); +} + +template<> +inline uint8_t +ConvertNumber(double src) +{ + return JS::ToUint8(src); +} + +template<> +inline uint8_clamped +ConvertNumber(double src) +{ + return uint8_clamped(src); +} + +template<> +inline int16_t +ConvertNumber(double src) +{ + return JS::ToInt16(src); +} + +template<> +inline uint16_t +ConvertNumber(double src) +{ + return JS::ToUint16(src); +} + +template<> +inline int32_t +ConvertNumber(double src) +{ + return JS::ToInt32(src); +} + +template<> +inline uint32_t +ConvertNumber(double src) +{ + return JS::ToUint32(src); +} + +template +inline To +ConvertNumber(From src) +{ + static_assert(!mozilla::IsFloatingPoint::value || + (mozilla::IsFloatingPoint::value && mozilla::IsFloatingPoint::value), + "conversion from floating point to int should have been handled by " + "specializations above"); + return To(src); +} + template struct TypeIDOfType; template<> struct TypeIDOfType { static const Scalar::Type id = Scalar::Int8; }; template<> struct TypeIDOfType { static const Scalar::Type id = Scalar::Uint8; }; @@ -196,50 +308,50 @@ class ElementSpecific case Scalar::Int8: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Uint8: case Scalar::Uint8Clamped: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Int16: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Uint16: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Int32: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Uint32: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Float32: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Float64: { SharedMem src = data.cast(); for (uint32_t i = 0; i < count; ++i) - Ops::store(dest++, T(Ops::load(src++))); + Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } default: @@ -351,50 +463,50 @@ class ElementSpecific case Scalar::Int8: { int8_t* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Uint8: case Scalar::Uint8Clamped: { uint8_t* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Int16: { int16_t* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Uint16: { uint16_t* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Int32: { int32_t* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Uint32: { uint32_t* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Float32: { float* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Float64: { double* src = static_cast(data); for (uint32_t i = 0; i < len; ++i) - Ops::store(dest++, T(*src++)); + Ops::store(dest++, ConvertNumber(*src++)); break; } default: diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 5071cd3ed3..e64b1f293c 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -16837,6 +16837,9 @@ "url": "/html/syntax/serializing-html-fragments/outerHTML.html" }, { + "path": "html/syntax/serializing-xml-fragments/outerHTML.html", + "url": "/html/syntax/serializing-xml-fragments/outerHTML.html" + }, "path": "html/webappapis/atob/base64.html", "url": "/html/webappapis/atob/base64.html" }, diff --git a/testing/web-platform/meta/html/browsers/the-window-object/window-named-properties.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/window-named-properties.html.ini deleted file mode 100644 index 6cc93d66cb..0000000000 --- a/testing/web-platform/meta/html/browsers/the-window-object/window-named-properties.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[window-named-properties.html] - type: testharness - [Static name on the prototype] - expected: FAIL - - [constructor] - expected: FAIL - diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/postmessage-to-client.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/postmessage-to-client.https.html.ini deleted file mode 100644 index 4ec9f05ade..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/postmessage-to-client.https.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[postmessage-to-client.https.html] - type: testharness - expected: TIMEOUT - [postMessage from ServiceWorker to Client] - expected: TIMEOUT - diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini new file mode 100644 index 0000000000..9b5cd518b8 --- /dev/null +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini @@ -0,0 +1,3 @@ +[update-after-navigation-fetch-event.https.html] + type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1226443 diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini index 2402f8e510..7eec4c67ce 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini @@ -1,3 +1,4 @@ [update-after-oneday.https.html] type: testharness prefs: [dom.serviceWorkers.testUpdateOverOneDay: true] + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1226443 diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html index d6191b0c01..4778f0c48a 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html @@ -13,6 +13,8 @@ var SERVICE_WORKER_SCRIPT = "resources/empty-worker.js"; var resolve_install_appcache = undefined; var reject_install_appcache = undefined; +var frames = []; + // Called by the INSTALL_APPCACHE_URL child frame. function notify_appcache_installed(success) { if (success) @@ -24,6 +26,7 @@ function notify_appcache_installed(success) { function install_appcache() { return new Promise(function(resolve, reject) { var frame = document.createElement('iframe'); + frames.push(frame); frame.src = INSTALL_APPCACHE_URL; document.body.appendChild(frame); resolve_install_appcache = function() { @@ -47,6 +50,7 @@ function notify_is_appcached(is) { function is_appcached() { return new Promise(function(resolve) { var frame = document.createElement('iframe'); + frames.push(frame); frame.src = IS_APPCACHED_URL; document.body.appendChild(frame); resolve_is_appcached = function(is) { @@ -77,6 +81,7 @@ async_test(function(t) { }) .then(function(result) { assert_false(result, 'but serviceworkers should take priority'); + frames.forEach(function(f) { f.remove(); }); service_worker_unregister_and_done(t, SERVICE_WORKER_SCOPE); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html index 66301fe940..f7581931ee 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html @@ -6,18 +6,24 @@ diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html index 89595d4d64..ff3b7ce04f 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html @@ -9,19 +9,34 @@ var t = async_test('controller is set for a controlled document'); t.step(function() { var url = 'resources/empty-worker.js'; var scope = 'resources/blank.html'; + var registration; + var controller; + var frame; service_worker_unregister_and_register(t, url, scope) - .then(t.step_func(function(registration) { + .then(t.step_func(function(swr) { + registration = swr; return wait_for_state(t, registration.installing, 'activated'); })) .then(t.step_func(function() { return with_iframe(scope) })) - .then(t.step_func(function(frame) { + .then(t.step_func(function(f) { + frame = f; var w = frame.contentWindow; - var controller = w.navigator.serviceWorker.controller; + controller = w.navigator.serviceWorker.controller; assert_true(controller instanceof w.ServiceWorker, 'controller should be a ServiceWorker object'); assert_equals(controller.scriptURL, normalizeURL(url)); + + // objects from different windows should not be equal + assert_not_equals(controller, registration.active); + + return w.navigator.serviceWorker.getRegistration(); + })) + .then(t.step_func(function(frameRegistration) { + // SW objects from same window should be equal + assert_equals(frameRegistration.active, controller); + frame.remove(); service_worker_unregister_and_done(t, scope); })) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-reload.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-reload.https.html index 9223b2ff8c..4490c70796 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-reload.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-reload.https.html @@ -8,6 +8,8 @@ promise_test(function(t) { var scope = 'resources/blank.html'; var frame; + var registration; + var controller; return service_worker_unregister(t, scope) .then(function() { return with_iframe(scope); @@ -17,7 +19,8 @@ promise_test(function(t) { return frame.contentWindow.navigator.serviceWorker.register( 'resources/empty-worker.js', {scope: scope}); }) - .then(function(registration) { + .then(function(swr) { + registration = swr; return wait_for_state(t, registration.installing, 'activated'); }) .then(function() { @@ -32,9 +35,17 @@ promise_test(function(t) { }) .then(function() { var w = frame.contentWindow; - assert_true( - w.navigator.serviceWorker.controller instanceof w.ServiceWorker, - 'controller should be a ServiceWorker object upon reload'); + controller = w.navigator.serviceWorker.controller; + assert_true(controller instanceof w.ServiceWorker, + 'controller should be a ServiceWorker object upon reload'); + + // objects from separate windows should not be equal + assert_not_equals(controller, registration.active); + + return w.navigator.serviceWorker.getRegistration(); + }) + .then(function(frameRegistration) { + assert_equals(frameRegistration.active, controller); frame.remove(); service_worker_unregister_and_done(t, scope); }); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html index 37833787ac..bdb56c213a 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html @@ -19,6 +19,7 @@ async_test(function(t) { var channel = new MessageChannel(); channel.port1.onmessage = t.step_func(function(e) { assert_equals(e.data.results, 'finish'); + frame.remove(); service_worker_unregister_and_done(t, SCOPE); }); frame.contentWindow.postMessage({}, diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html index b53a4a41bc..556d04413f 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html @@ -52,14 +52,12 @@ function redirect_fetch_test(t, test) { var p = new Promise(function(resolve, reject) { var channel = new MessageChannel(); channel.port1.onmessage = function(e) { + frame.remove(); if (e.data.result === 'reject') { - frame.remove(); reject(e.data.detail); } else if (e.data.result === 'success') { - frame.remove(); resolve(e.data.result); } else { - frame.remove(); resolve(e.data.detail); } }; diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html index dc0dba38a5..76104600e0 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html @@ -186,7 +186,7 @@ async_test(function(t) { assert_equals(frame.contentDocument.body.textContent, 'POST:application/x-www-form-urlencoded:' + 'testName1=testValue1&testName2=testValue2'); - document.body.removeChild(frame); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html index 99df2463d9..cc1dac472b 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html @@ -74,12 +74,13 @@ function getLoadedWindowAsObject(win) { async_test(function(t) { var scope = 'resources/fetch-frame-resource/frame-basic'; + var frame; service_worker_unregister_and_register(t, worker, scope) .then(function(reg) { return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope + '?url=' + encodeURIComponent(host_info['HTTPS_ORIGIN'] + path); @@ -91,6 +92,7 @@ async_test(function(t) { result.jsonpResult, 'success', 'Basic type response could be loaded in the iframe.'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); @@ -98,12 +100,13 @@ async_test(function(t) { async_test(function(t) { var scope = 'resources/fetch-frame-resource/frame-cors'; + var frame; service_worker_unregister_and_register(t, worker, scope) .then(function(reg) { return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope + '?mode=cors&url=' + encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path + @@ -116,6 +119,7 @@ async_test(function(t) { result.jsonpResult, 'success', 'CORS type response could be loaded in the iframe.'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); @@ -123,12 +127,13 @@ async_test(function(t) { async_test(function(t) { var scope = 'resources/fetch-frame-resource/frame-opaque'; + var frame; service_worker_unregister_and_register(t, worker, scope) .then(function(reg) { return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope + '?mode=no-cors&url=' + encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path); @@ -140,6 +145,7 @@ async_test(function(t) { result, null, 'Opaque type response could not be loaded in the iframe.'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html index e76ded4026..229d585c3c 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html @@ -9,6 +9,7 @@ var worker = 'resources/fetch-rewrite-worker.js'; var path = base_path() + 'resources/fetch-access-control.py'; var host_info = get_host_info(); + var frame; async_test(function(t) { var scope = 'resources/fetch-header-visibility-iframe.html'; @@ -17,7 +18,7 @@ return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope; document.body.appendChild(frame); @@ -42,6 +43,7 @@ }); }) .then(function(result) { + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html index d2b0eb9ed8..304680c97e 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html @@ -54,6 +54,7 @@ async_test(function(t) { expected_url, 'frame should now be loaded and controlled'); assert_equals(registration.active.state, 'activated', 'active worker should be in activated state'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }).catch(unreached_rejection(t)); }, 'Fetch events should wait for the activate event to complete.'); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html index cba4479488..38b4f56e78 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html @@ -4,6 +4,7 @@
+ + +
- \ No newline at end of file + diff --git a/testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html b/testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html new file mode 100644 index 0000000000..334ce8adf7 --- /dev/null +++ b/testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html @@ -0,0 +1,38 @@ + + + + HTML Test: element.outerHTML to verify XML fragment serialization algorithm + + + + + + + + +
+ + + diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index d7e52b05d1..c452d79072 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -8704,6 +8704,13 @@ "kind": "count", "description": "Count slow script notices" }, + "SLOW_SCRIPT_PAGE_COUNT": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], + "expires_in_version": "never", + "kind": "count", + "bug_numbers": [1251667], + "description": "The number of pages that trigger slow script notices" + }, "PLUGIN_HANG_NOTICE_COUNT": { "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index 535366046a..4e7b90a96e 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -552,9 +552,17 @@ /* * A success code that indicates that evaluating a string of JS went - * just fine except it threw an exception. + * just fine except it threw an exception. Only for legacy use by + * nsJSUtils. */ ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW, SUCCESS(2)), + + /* + * A success code that indicates that evaluating a string of JS went + * just fine except it was killed by an uncatchable exception. + * Only for legacy use by nsJSUtils. + */ + ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE, SUCCESS(3)), #undef MODULE