From aadb31cb88463acc501bb6cee036c1632d6f66d0 Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Thu, 31 May 2018 15:23:51 +0800 Subject: [PATCH] import changes from tenfourfox-history: - #374, #446: M1354080 (2d2a85336) - make it only a warning on non-intel (c236974a3) - factor code constants to a tag and make it endianness dependent (9539a6422) - closes #374: asynchronous SVG through (a87df728a) - closes #391: M1342721 with some ideas from M1342439 (dd4ffb4f1) - #425: M1385478 IsRequired() (07b90443f) - closes #425: faster ToLowerCase() with ideas from M1383647 (5963b7a79) - #432: M1390402 partial (without semantic changes) plus other partial cleanup (e2a616378) --- dom/animation/AnimationTimeline.h | 11 +++ dom/base/nsDocument.cpp | 12 ++- dom/base/nsGenericDOMDataNode.cpp | 2 +- dom/base/nsGenericDOMDataNode.h | 5 ++ dom/base/nsGlobalWindow.cpp | 4 +- dom/bindings/BindingUtils.cpp | 4 +- dom/events/EventStates.h | 4 +- dom/html/HTMLFormElement.cpp | 12 ++- dom/html/HTMLInputElement.cpp | 29 +++--- dom/html/HTMLInputElement.h | 21 +++-- dom/html/HTMLSelectElement.cpp | 15 ++-- dom/html/HTMLSelectElement.h | 6 +- dom/html/HTMLTextAreaElement.cpp | 15 ++-- dom/html/HTMLTextAreaElement.h | 4 +- dom/html/nsGenericHTMLElement.cpp | 19 ++++ dom/html/nsGenericHTMLElement.h | 5 ++ dom/ipc/TabParent.cpp | 16 ---- dom/ipc/TabParent.h | 4 - editor/libeditor/nsEditor.cpp | 29 ------ editor/libeditor/nsEditor.h | 24 ++++- editor/libeditor/nsTextEditRules.cpp | 5 +- gfx/2d/ScaledFontMac.cpp | 8 +- image/SVGDocumentWrapper.cpp | 33 ++++++- image/VectorImage.cpp | 6 ++ js/src/irregexp/RegExpEngine.cpp | 2 + js/src/jscntxt.h | 2 + js/src/jscntxtinlines.h | 128 +++++++++++++++++++++++++-- js/src/jsfriendapi.cpp | 19 +++- js/src/jsfriendapi.h | 86 ++---------------- js/src/jsstr.cpp | 70 +++++++++++++-- js/xpconnect/src/XPCVariant.cpp | 4 +- layout/base/nsDisplayList.cpp | 7 +- 32 files changed, 407 insertions(+), 204 deletions(-) diff --git a/dom/animation/AnimationTimeline.h b/dom/animation/AnimationTimeline.h index 7163d4350..150f8f5bc 100644 --- a/dom/animation/AnimationTimeline.h +++ b/dom/animation/AnimationTimeline.h @@ -94,6 +94,17 @@ public: */ virtual void NotifyAnimationUpdated(Animation& aAnimation); + /** + * Returns true if any CSS animations, CSS transitions or Web animations are + * currently associated with this timeline. As soon as an animation is + * applied to an element it is associated with the timeline even if it has a + * delayed start, so this includes animations that may not be active for some + * time. + */ + bool HasAnimations() const { + return !mAnimations.IsEmpty(); + } + void RemoveAnimation(Animation* aAnimation); protected: diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 66f4a8aca..445afa0d2 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -8382,7 +8382,11 @@ nsDocument::AddToRadioGroup(const nsAString& aName, nsCOMPtr element = do_QueryInterface(aRadio); NS_ASSERTION(element, "radio controls have to be content elements"); - if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + + HTMLInputElement* input = HTMLInputElement::FromContent(element); + NS_ASSERTION(input, "radio controls have to be input elements!"); + + if (input->IsRequired()) { radioGroup->mRequiredRadioCount++; } } @@ -8396,7 +8400,11 @@ nsDocument::RemoveFromRadioGroup(const nsAString& aName, nsCOMPtr element = do_QueryInterface(aRadio); NS_ASSERTION(element, "radio controls have to be content elements"); - if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + + HTMLInputElement* input = HTMLInputElement::FromContent(element); + NS_ASSERTION(input, "radio controls have to be input elements!"); + + if (input->IsRequired()) { NS_ASSERTION(radioGroup->mRequiredRadioCount != 0, "mRequiredRadioCount about to wrap below 0!"); radioGroup->mRequiredRadioCount--; diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index 3040fcb41..91c0b5f5d 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -979,7 +979,7 @@ nsGenericDOMDataNode::GetText() uint32_t nsGenericDOMDataNode::TextLength() const { - return mText.GetLength(); + return TextDataLength(); } nsresult diff --git a/dom/base/nsGenericDOMDataNode.h b/dom/base/nsGenericDOMDataNode.h index 0b60860bc..069748272 100644 --- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -219,6 +219,11 @@ public: rv = ReplaceData(aOffset, aCount, aData); } + uint32_t TextDataLength() const + { + return mText.GetLength(); + } + //---------------------------------------- #ifdef DEBUG diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index e80ba6ab9..179aeadf9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2396,9 +2396,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, // transplanting code, since it has no good way to handle errors. This uses // the untrusted script limit, which is not strictly necessary since no // actual script should run. - bool overrecursed = false; - JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, overrecursed = true); - if (overrecursed) { + if (MOZ_UNLIKELY(!js::CheckRecursionConservativeDontReport(cx))) { NS_WARNING("Overrecursion in SetNewDocument"); return NS_ERROR_FAILURE; } diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index f08cc56d0..3549303c3 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1891,7 +1891,9 @@ ReparentWrapper(JSContext* aCx, JS::Handle aObjArg) // transplanting code, since it has no good way to handle errors. This uses // the untrusted script limit, which is not strictly necessary since no // actual script should run. - JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE); + if (MOZ_UNLIKELY(!js::CheckRecursionConservative(aCx))) { + return NS_ERROR_FAILURE; + } JS::Rooted aObj(aCx, aObjArg); const DOMJSClass* domClass = GetDOMClass(aObj); diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h index fe8f0315b..b65b01e21 100644 --- a/dom/events/EventStates.h +++ b/dom/events/EventStates.h @@ -278,11 +278,13 @@ private: #define DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED) +#define REQUIRED_STATES (NS_EVENT_STATE_REQUIRED | NS_EVENT_STATE_OPTIONAL) + #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \ NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \ NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \ NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | \ - DISABLED_STATES | \ + DISABLED_STATES | REQUIRED_STATES | \ NS_EVENT_STATE_UNRESOLVED) #define INTRINSIC_STATES (~ESM_MANAGED_STATES) diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 1e99558ce..421a3bf53 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -2296,7 +2296,10 @@ HTMLFormElement::AddToRadioGroup(const nsAString& aName, nsCOMPtr element = do_QueryInterface(aRadio); NS_ASSERTION(element, "radio controls have to be content elements!"); - if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + HTMLInputElement* input = HTMLInputElement::FromContent(element); + NS_ASSERTION(input, "radio controls have to be input elements!"); + + if (input->IsRequired()) { mRequiredRadioButtonCounts.Put(aName, mRequiredRadioButtonCounts.Get(aName)+1); } @@ -2307,9 +2310,12 @@ HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName, nsIFormControl* aRadio) { nsCOMPtr element = do_QueryInterface(aRadio); - NS_ASSERTION(element, "radio controls have to be content elements!"); + NS_ASSERTION(element, "radio controls have to be content elements"); - if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + HTMLInputElement* input = HTMLInputElement::FromContent(element); + NS_ASSERTION(input, "radio controls have to be input elements!"); + + if (input->IsRequired()) { uint32_t requiredNb = mRequiredRadioButtonCounts.Get(aName); NS_ASSERTION(requiredNb >= 1, "At least one radio button has to be required!"); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 24a88ce53..9e544bd3b 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -1149,6 +1149,13 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, UpdateDisabledState(aNotify); } + if (aName == nsGkAtoms::required && DoesRequiredApply()) { + // This *has* to be called *before* UpdateValueMissingValidityState + // because UpdateValueMissingValidityState depends on our required + // state. + UpdateRequiredState(!!aValue, aNotify); + } + UpdateValueMissingValidityState(); // This *has* to be called *after* validity has changed. @@ -4371,6 +4378,15 @@ HTMLInputElement::HandleTypeChange(uint8_t aNewType) mFocusedValue.Truncate(); } + // Update or clear our required states since we may have changed from a + // required input type to a non-required input type or viceversa. + if (DoesRequiredApply()) { + bool isRequired = HasAttr(kNameSpaceID_None, nsGkAtoms::required); + UpdateRequiredState(isRequired, false); // See below for why this is OK. + } else { + RemoveStatesSilently(REQUIRED_STATES); + } + UpdateHasRange(); // Do not notify, it will be done after if needed. @@ -5757,12 +5773,6 @@ HTMLInputElement::IntrinsicState() const state |= nsImageLoadingContent::ImageState(); } - if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { - state |= NS_EVENT_STATE_REQUIRED; - } else { - state |= NS_EVENT_STATE_OPTIONAL; - } - if (IsCandidateForConstraintValidation()) { if (IsValid()) { state |= NS_EVENT_STATE_VALID; @@ -6369,8 +6379,7 @@ HTMLInputElement::IsValueMissing() const // Should use UpdateValueMissingValidityStateForRadio() for type radio. MOZ_ASSERT(mType != NS_FORM_INPUT_RADIO); - if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || - !DoesRequiredApply()) { + if (!IsRequired() || !DoesRequiredApply()) { return false; } @@ -6635,7 +6644,7 @@ HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf) // If there is no selection, that might mean the radio is not in a group. // In that case, we can look for the checked state of the radio. bool selected = selection || (!aIgnoreSelf && mChecked); - bool required = !aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required); + bool required = !aIgnoreSelf && IsRequired(); bool valueMissing = false; nsCOMPtr container = GetRadioGroupContainer(); @@ -6652,7 +6661,7 @@ HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf) // If the current radio is required and not ignored, we can assume the entire // group is required. if (!required) { - required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) + required = (aIgnoreSelf && IsRequired()) ? container->GetRequiredRadioCount(name) - 1 : container->GetRequiredRadioCount(name); } diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 7a297ada0..6fdea0195 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -774,6 +774,22 @@ public: */ static Decimal StringToDecimal(const nsAString& aValue); + /** + * Returns if the required attribute applies for the current type. + */ + bool DoesRequiredApply() const; + + /** + * Returns the current required state of the element. This function differs + * from Required() in that this function only returns true for input types + * that @required attribute applies and the attribute is set; in contrast, + * Required() returns true whenever @required attribute is set. + */ + bool IsRequired() const + { + return State().HasState(NS_EVENT_STATE_REQUIRED); + } + protected: virtual ~HTMLInputElement(); @@ -964,11 +980,6 @@ protected: */ bool DoesReadOnlyApply() const; - /** - * Returns if the required attribute applies for the current type. - */ - bool DoesRequiredApply() const; - /** * Returns if the pattern attribute applies for the current type. */ diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index d4dbec5ab..7e2fb5a32 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -1070,7 +1070,7 @@ HTMLSelectElement::IsOptionDisabled(int32_t aIndex, bool* aIsDisabled) } bool -HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption) +HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption) const { MOZ_ASSERT(aOption); if (aOption->Disabled()) { @@ -1301,6 +1301,11 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, UpdateValueMissingValidityState(); UpdateBarredFromConstraintValidation(); } else if (aName == nsGkAtoms::required) { + // This *has* to be called *before* UpdateValueMissingValidityState + // because UpdateValueMissingValidityState depends on our required + // state. + UpdateRequiredState(!!aValue, aNotify); + UpdateValueMissingValidityState(); } else if (aName == nsGkAtoms::autocomplete) { // Clear the cached @autocomplete attribute state @@ -1523,12 +1528,6 @@ HTMLSelectElement::IntrinsicState() const } } - if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { - state |= NS_EVENT_STATE_REQUIRED; - } else { - state |= NS_EVENT_STATE_OPTIONAL; - } - return state; } @@ -1771,7 +1770,7 @@ HTMLSelectElement::RebuildOptionsArray(bool aNotify) } bool -HTMLSelectElement::IsValueMissing() +HTMLSelectElement::IsValueMissing() const { if (!Required()) { return false; diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h index 61c2db61b..b3e7885eb 100644 --- a/dom/html/HTMLSelectElement.h +++ b/dom/html/HTMLSelectElement.h @@ -198,7 +198,7 @@ public: } bool Required() const { - return GetBoolAttr(nsGkAtoms::required); + return State().HasState(NS_EVENT_STATE_REQUIRED); } void SetRequired(bool aVal, ErrorResult& aRv) { @@ -332,7 +332,7 @@ public: */ NS_IMETHOD IsOptionDisabled(int32_t aIndex, bool* aIsDisabled); - bool IsOptionDisabled(HTMLOptionElement* aOption); + bool IsOptionDisabled(HTMLOptionElement* aOption) const; /** * Sets multiple options (or just sets startIndex if select is single) @@ -505,7 +505,7 @@ protected: // nsIConstraintValidation void UpdateBarredFromConstraintValidation(); - bool IsValueMissing(); + bool IsValueMissing() const; /** * Get the index of the first option at, under or following the content in diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 47e36187f..a8d702b77 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -1136,12 +1136,6 @@ HTMLTextAreaElement::IntrinsicState() const { EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState(); - if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { - state |= NS_EVENT_STATE_REQUIRED; - } else { - state |= NS_EVENT_STATE_OPTIONAL; - } - if (IsCandidateForConstraintValidation()) { if (IsValid()) { state |= NS_EVENT_STATE_VALID; @@ -1286,6 +1280,13 @@ HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, UpdateDisabledState(aNotify); } + if (aName == nsGkAtoms::required) { + // This *has* to be called *before* UpdateValueMissingValidityState + // because UpdateValueMissingValidityState depends on our required + // state. + UpdateRequiredState(!!aValue, aNotify); + } + UpdateValueMissingValidityState(); // This *has* to be called *after* validity has changed. @@ -1368,7 +1369,7 @@ HTMLTextAreaElement::IsTooLong() bool HTMLTextAreaElement::IsValueMissing() const { - if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) { + if (!Required() || !IsMutable()) { return false; } diff --git a/dom/html/HTMLTextAreaElement.h b/dom/html/HTMLTextAreaElement.h index 318999713..ff45726b0 100644 --- a/dom/html/HTMLTextAreaElement.h +++ b/dom/html/HTMLTextAreaElement.h @@ -222,9 +222,9 @@ public: { SetHTMLBoolAttr(nsGkAtoms::readonly, aReadOnly, aError); } - bool Required() + bool Required() const { - return GetBoolAttr(nsGkAtoms::required); + return State().HasState(NS_EVENT_STATE_REQUIRED); } void SetRangeText(const nsAString& aReplacement, ErrorResult& aRv); diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index ae6eed02f..552a7d99e 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -107,6 +107,7 @@ #include "mozilla/dom/HTMLBodyElement.h" #include "imgIContainer.h" #include "nsComputedDOMStyle.h" +#include "mozilla/dom/HTMLInputElement.h" using namespace mozilla; using namespace mozilla::dom; @@ -2640,6 +2641,24 @@ void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify) } } +void +nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired, bool aNotify) +{ + EventStates requiredStates; + if (aIsRequired) { + requiredStates |= NS_EVENT_STATE_REQUIRED; + } else { + requiredStates |= NS_EVENT_STATE_OPTIONAL; + } + + EventStates oldRequiredStates = State() & REQUIRED_STATES; + EventStates changedStates = requiredStates ^ oldRequiredStates; + + if (!changedStates.IsEmpty()) { + ToggleStates(changedStates, aNotify); + } +} + void nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify) { diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index aeab24224..3dd4161a7 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -1351,6 +1351,11 @@ public: */ void UpdateDisabledState(bool aNotify); + /** + * Update our required/optional flags to match the given aIsRequired boolean. + */ + void UpdateRequiredState(bool aIsRequired, bool aNotify); + void FieldSetFirstLegendChanged(bool aNotify) { UpdateFieldSet(aNotify); } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 2ec5abb49..a239b848d 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -383,10 +383,6 @@ TabParent::AddWindowListeners() this, false, false); } } - if (nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell()) { - mPresShellWithRefreshListener = shell; - shell->AddPostRefreshObserver(this); - } } } @@ -401,18 +397,6 @@ TabParent::RemoveWindowListeners() this, false); } } - if (mPresShellWithRefreshListener) { - mPresShellWithRefreshListener->RemovePostRefreshObserver(this); - mPresShellWithRefreshListener = nullptr; - } -} - -void -TabParent::DidRefresh() -{ - if (mChromeOffset != -GetChildProcessOffset()) { - UpdatePosition(); - } } void diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 1bc1aeb0b..923504b42 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -85,7 +85,6 @@ class TabParent final : public PBrowserParent , public nsISecureBrowserUI , public nsSupportsWeakReference , public TabContext - , public nsAPostRefreshObserver , public nsIWebBrowserPersistable { typedef mozilla::dom::ClonedMessageData ClonedMessageData; @@ -145,7 +144,6 @@ public: void RemoveWindowListeners(); void AddWindowListeners(); - void DidRefresh() override; virtual bool RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigation) override; @@ -628,8 +626,6 @@ private: // cursor. This happens whenever the cursor is in the tab's region. bool mTabSetsCursor; - RefPtr mPresShellWithRefreshListener; - bool mHasContentOpener; DebugOnly mActiveSupressDisplayportCount; diff --git a/editor/libeditor/nsEditor.cpp b/editor/libeditor/nsEditor.cpp index 4bb4ba4a8..dfe99d8ed 100644 --- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -3473,15 +3473,6 @@ nsEditor::IsEditable(nsINode* aNode) } } -bool -nsEditor::IsMozEditorBogusNode(nsINode* element) -{ - return element && element->IsElement() && - element->AsElement()->AttrValueIs(kNameSpaceID_None, - kMOZEditorBogusNodeAttrAtom, kMOZEditorBogusNodeValue, - eCaseMatters); -} - uint32_t nsEditor::CountEditableChildren(nsINode* aNode) { @@ -3625,12 +3616,6 @@ nsEditor::IsTextNode(nsIDOMNode *aNode) return (nodeType == nsIDOMNode::TEXT_NODE); } -bool -nsEditor::IsTextNode(nsINode *aNode) -{ - return aNode->NodeType() == nsIDOMNode::TEXT_NODE; -} - /////////////////////////////////////////////////////////////////////////// // GetChildAt: returns the node at this position index in the parent // @@ -4844,20 +4829,6 @@ nsEditor::FinalizeSelection() return NS_OK; } -dom::Element * -nsEditor::GetRoot() -{ - if (!mRootElement) - { - nsCOMPtr root; - - // Let GetRootElement() do the work - GetRootElement(getter_AddRefs(root)); - } - - return mRootElement; -} - dom::Element* nsEditor::GetEditorRoot() { diff --git a/editor/libeditor/nsEditor.h b/editor/libeditor/nsEditor.h index feb3ed21a..64e6b1b89 100644 --- a/editor/libeditor/nsEditor.h +++ b/editor/libeditor/nsEditor.h @@ -569,7 +569,13 @@ public: virtual bool IsEditable(nsINode* aNode); /** returns true if aNode is a MozEditorBogus node */ - bool IsMozEditorBogusNode(nsINode* aNode); + bool IsMozEditorBogusNode(nsINode* aNode) + { + return aNode && aNode->IsElement() && + aNode->AsElement()->AttrValueIs(kNameSpaceID_None, + kMOZEditorBogusNodeAttrAtom, kMOZEditorBogusNodeValue, + eCaseMatters); + } /** counts number of editable child nodes */ uint32_t CountEditableChildren(nsINode* aNode); @@ -598,7 +604,10 @@ public: virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2); static bool IsTextNode(nsIDOMNode *aNode); - static bool IsTextNode(nsINode *aNode); + static bool IsTextNode(nsINode* aNode) + { + return aNode->NodeType() == nsIDOMNode::TEXT_NODE; + } static nsCOMPtr GetChildAt(nsIDOMNode *aParent, int32_t aOffset); static nsCOMPtr GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset); @@ -664,7 +673,16 @@ public: virtual already_AddRefed GetDOMEventTarget() = 0; // Fast non-refcounting editor root element accessor - mozilla::dom::Element *GetRoot(); + mozilla::dom::Element *GetRoot() + { + if (!mRootElement) { + // Let GetRootElement() do the work + nsCOMPtr root; + GetRootElement(getter_AddRefs(root)); + } + + return mRootElement; + } // Likewise, but gets the editor's root instead, which is different for HTML // editors diff --git a/editor/libeditor/nsTextEditRules.cpp b/editor/libeditor/nsTextEditRules.cpp index 6d8ab6669..788f1f2a7 100644 --- a/editor/libeditor/nsTextEditRules.cpp +++ b/editor/libeditor/nsTextEditRules.cpp @@ -1176,11 +1176,12 @@ nsTextEditRules::CreateBogusNodeIfNeeded(Selection* aSelection) // Now we've got the body element. Iterate over the body element's children, // looking for editable content. If no editable content is found, insert the // bogus node. - for (nsCOMPtr bodyChild = body->GetFirstChild(); + bool bodyEditable = mEditor->IsEditable(body); + for (nsIContent* bodyChild = body->GetFirstChild(); bodyChild; bodyChild = bodyChild->GetNextSibling()) { if (mEditor->IsMozEditorBogusNode(bodyChild) || - !mEditor->IsEditable(body) || // XXX hoist out of the loop? + !bodyEditable || mEditor->IsEditable(bodyChild) || mEditor->IsBlockNode(bodyChild)) { return NS_OK; } diff --git a/gfx/2d/ScaledFontMac.cpp b/gfx/2d/ScaledFontMac.cpp index 65ae92c22..6efd3479e 100644 --- a/gfx/2d/ScaledFontMac.cpp +++ b/gfx/2d/ScaledFontMac.cpp @@ -209,6 +209,12 @@ struct writeBuf int offset; }; +#ifdef __ppc__ +#define TAG_CFF 0x43464620 +#else +#define TAG_CFF 0x20464643 +#endif + bool ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) { @@ -223,7 +229,7 @@ ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) bool CFF = false; for (CFIndex i = 0; iGetDocument(); - return doc && doc->HasAnimationController() && - doc->GetAnimationController()->HasRegisteredAnimations(); + if (!doc) { + return false; + } + if (doc->Timeline()->HasAnimations()) { + // CSS animations (technically HasAnimations() also checks for CSS + // transitions and Web animations but since SVG-as-an-image doesn't run + // script they will never run in the document that we wrap). + return true; + } + if (doc->HasAnimationController() && + doc->GetAnimationController()->HasRegisteredAnimations()) { + // SMIL animations + return true; + } + return false; } void @@ -330,6 +345,20 @@ SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest, NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED); + // Create a navigation time object and pass it to the SVG document through + // the viewer. + // The timeline(DocumentTimeline, used in CSS animation) of this SVG + // document needs this navigation timing object for time computation, such + // as to calculate current time stamp based on the start time of navigation + // time object. + // + // For a root document, DocShell would do these sort of things + // automatically. Since there is no DocShell for this wrapped SVG document, + // we must set it up manually. + RefPtr timing = new nsDOMNavigationTiming(); + timing->NotifyNavigationStart(); + viewer->SetNavigationTiming(timing); + nsCOMPtr parser = do_QueryInterface(listener); NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED); diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index 0703f18c2..e7a46712f 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -524,6 +524,12 @@ VectorImage::RequestRefresh(const TimeStamp& aTime) return; } + PendingAnimationTracker* tracker = + mSVGDocumentWrapper->GetDocument()->GetPendingAnimationTracker(); + if (tracker && ShouldAnimate()) { + tracker->TriggerPendingAnimationsOnNextTick(aTime); + } + EvaluateAnimation(); mSVGDocumentWrapper->TickRefreshDriver(); diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 69da8912c..fb9365209 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -28,6 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "jscntxtinlines.h" + #include "irregexp/RegExpEngine.h" #include "irregexp/NativeRegExpMacroAssembler.h" diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 89bf28635..134fa15e4 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -149,6 +149,8 @@ class ExclusiveContext : public ContextFriendFields, return isJSContext(); } + JSRuntime* ecRuntime() const { return runtime_; } // TenFourFox issue 391 + bool runtimeMatches(JSRuntime* rt) const { return runtime_ == rt; } diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 073a04c7b..cd5bdc1ae 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -19,8 +19,130 @@ #include "vm/ProxyObject.h" #include "vm/Symbol.h" +MOZ_ALWAYS_INLINE bool +JSContext::runningWithTrustedPrincipals() const +{ + return !compartment() || compartment()->principals() == runtime()->trustedPrincipals(); +} + namespace js { +MOZ_ALWAYS_INLINE uintptr_t +GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) +{ + PerThreadDataFriendFields* mainThread = + PerThreadDataFriendFields::getMainThread(GetRuntime(cx)); + uintptr_t limit = mainThread->nativeStackLimit[kind]; +#if JS_STACK_GROWTH_DIRECTION > 0 + limit += extraAllowance; +#else + limit -= extraAllowance; +#endif + return limit; +} + +// Needed for issue 391 -- see below +MOZ_ALWAYS_INLINE uintptr_t +GetNativeStackLimit(ExclusiveContext* cx, StackKind kind, int extraAllowance = 0) +{ + PerThreadDataFriendFields* mainThread = + PerThreadDataFriendFields::getMainThread(cx->ecRuntime()); + uintptr_t limit = mainThread->nativeStackLimit[kind]; +#if JS_STACK_GROWTH_DIRECTION > 0 + limit += extraAllowance; +#else + limit -= extraAllowance; +#endif + return limit; +} + +MOZ_ALWAYS_INLINE uintptr_t +GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) +{ + StackKind kind = cx->runningWithTrustedPrincipals() ? StackForTrustedScript + : StackForUntrustedScript; + return GetNativeStackLimit(cx, kind, extraAllowance); +} + +/* + * These macros report a stack overflow and run |onerror| if we are close to + * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a + * little extra space so that we can ensure that crucial code is able to run. + * JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check, + * including a safety buffer (as in, it uses the untrusted limit and subtracts + * a little more from it). + */ + +// Implement a fast path a la bug 1342439, but without all that churn. +// Leave the old limit versions here just in case. +// TenFourFox issue 391 + +#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx, js::StackForUntrustedScript), &stackDummy_))) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_))) {\ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx, js::StackForUntrustedScript), &stackDummy_))) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_))) {\ + onerror; \ + } \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \ + JS_BEGIN_MACRO \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \ + JS_BEGIN_MACRO \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror) + +#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, \ + js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ + onerror) + +#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \ + js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ + onerror) + class CompartmentChecker { JSCompartment* compartment; @@ -382,12 +504,6 @@ JSContext::setPendingException(js::Value v) MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment()); } -inline bool -JSContext::runningWithTrustedPrincipals() const -{ - return !compartment() || compartment()->principals() == runtime()->trustedPrincipals(); -} - inline void js::ExclusiveContext::enterCompartment(JSCompartment* c) { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a808545eb..0ad807e81 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -385,9 +385,24 @@ js::IsObjectInContextCompartment(JSObject* obj, const JSContext* cx) } JS_FRIEND_API(bool) -js::RunningWithTrustedPrincipals(JSContext* cx) +js::CheckRecursion(JSContext* cx) { - return cx->runningWithTrustedPrincipals(); + JS_CHECK_RECURSION(cx, return false); + return true; +} + +JS_FRIEND_API(bool) +js::CheckRecursionConservative(JSContext* cx) +{ + JS_CHECK_RECURSION_CONSERVATIVE(cx, return false); + return true; +} + +JS_FRIEND_API(bool) +js::CheckRecursionConservativeDontReport(JSContext* cx) +{ + JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, return false); + return true; } JS_FRIEND_API(JSFunction*) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 5ca11ac6d..41f73b8bd 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -967,89 +967,13 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx); #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */ JS_FRIEND_API(bool) -RunningWithTrustedPrincipals(JSContext* cx); +CheckRecursion(JSContext* cx); -MOZ_ALWAYS_INLINE uintptr_t -GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) -{ - PerThreadDataFriendFields* mainThread = - PerThreadDataFriendFields::getMainThread(GetRuntime(cx)); - uintptr_t limit = mainThread->nativeStackLimit[kind]; -#if JS_STACK_GROWTH_DIRECTION > 0 - limit += extraAllowance; -#else - limit -= extraAllowance; -#endif - return limit; -} +JS_FRIEND_API(bool) +CheckRecursionConservative(JSContext* cx); -MOZ_ALWAYS_INLINE uintptr_t -GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) -{ - StackKind kind = RunningWithTrustedPrincipals(cx) ? StackForTrustedScript - : StackForUntrustedScript; - return GetNativeStackLimit(cx, kind, extraAllowance); -} - -/* - * These macros report a stack overflow and run |onerror| if we are close to - * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a - * little extra space so that we can ensure that crucial code is able to run. - * JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check, - * including a safety buffer (as in, it uses the untrusted limit and subtracts - * a little more from it). - */ - -#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \ - JS_BEGIN_MACRO \ - int stackDummy_; \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ - js::ReportOverRecursed(cx); \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_RECURSION(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx), onerror) - -#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \ - JS_BEGIN_MACRO \ - int stackDummy_; \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, js::GetNativeStackLimit(cx), onerror) - -#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \ - JS_BEGIN_MACRO \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \ - JS_BEGIN_MACRO \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ - js::ReportOverRecursed(cx); \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror) - -#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT(cx, \ - js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ - onerror) - -#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \ - js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ - onerror) +JS_FRIEND_API(bool) +CheckRecursionConservativeDontReport(JSContext* cx); JS_FRIEND_API(void) StartPCCountProfiling(JSContext* cx); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 38ac4b931..da0841bf5 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -603,6 +603,64 @@ js::SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t l return NewDependentString(cx, str, begin, len); } +// Adapted from bug 1383647 +static inline bool +FastLatin1LowerCase(Latin1Char ch) +{ + if (MOZ_LIKELY(ch < 128)) + return ch >= 'A' && ch <= 'Z'; + // U+00C0 to U+00DE, except U+00D7, have a lowercase form. + bool canLower = ((ch & ~0x1F) == /* LATIN_CAPITAL_LETTER_A_WITH_GRAVE */ 0xc0) && + ((ch & /* MULTIPLICATION_SIGN */ 0xd7) != 0xd7); + MOZ_ASSERT(canLower == CanLowerCase(char16_t(ch))); + return canLower; +} + +static JSString* +ToLowerCaseLatin1(JSContext* cx, JSLinearString* str) +{ + // Unlike toUpperCase, toLowerCase has the nice invariant that if the input + // is a Latin1 string, the output is also a Latin1 string. + UniquePtr newChars; + size_t length = str->length(); + { + AutoCheckCannotGC nogc; + const Latin1Char* chars = str->chars(nogc); + + // Look for the first upper case character. + size_t i = 0; + for (; i < length; i++) { + if (FastLatin1LowerCase(chars[i])) + break; + } + + // If all characters are lower case, return the input string. + if (i == length) + return str; + + newChars = cx->make_pod_array(length + 1); + if (MOZ_UNLIKELY(!newChars)) + return nullptr; + + PodCopy(newChars.get(), chars, i); + + for (; i < length; i++) { + char16_t c = unicode::ToLowerCase(chars[i]); + MOZ_ASSERT_IF((IsSame::value), c <= JSString::MAX_LATIN1_CHAR); + newChars[i] = c; + } + + newChars[length] = 0; + } + + JSString* res = NewStringDontDeflate(cx, newChars.get(), length); + if (MOZ_UNLIKELY(!res)) + return nullptr; + + newChars.release(); + return res; +} + template static JSString* ToLowerCase(JSContext* cx, JSLinearString* str) @@ -628,7 +686,7 @@ ToLowerCase(JSContext* cx, JSLinearString* str) return str; newChars = cx->make_pod_array(length + 1); - if (!newChars) + if (MOZ_UNLIKELY(!newChars)) return nullptr; PodCopy(newChars.get(), chars, i); @@ -643,7 +701,7 @@ ToLowerCase(JSContext* cx, JSLinearString* str) } JSString* res = NewStringDontDeflate(cx, newChars.get(), length); - if (!res) + if (MOZ_UNLIKELY(!res)) return nullptr; newChars.release(); @@ -654,18 +712,18 @@ static inline bool ToLowerCaseHelper(JSContext* cx, CallReceiver call) { RootedString str(cx, ThisToStringForStringProto(cx, call)); - if (!str) + if (MOZ_UNLIKELY(!str)) return false; JSLinearString* linear = str->ensureLinear(cx); - if (!linear) + if (MOZ_UNLIKELY(!linear)) return false; if (linear->hasLatin1Chars()) - str = ToLowerCase(cx, linear); + str = ToLowerCaseLatin1(cx, linear); else str = ToLowerCase(cx, linear); - if (!str) + if (MOZ_UNLIKELY(!str)) return false; call.rval().setString(str); diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index 4eb8eee7e..d37d0eb01 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -258,7 +258,9 @@ XPCArrayHomogenizer::GetTypeForArray(JSContext* cx, HandleObject array, bool XPCVariant::InitializeData(JSContext* cx) { - JS_CHECK_RECURSION(cx, return false); + if (MOZ_UNLIKELY(!js::CheckRecursion(cx))) { + return false; + } RootedValue val(cx, GetJSVal()); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 91d9932d5..1cf03e3b5 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1619,7 +1619,7 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB nsIFrame* frame = aBuilder->RootReferenceFrame(); nsPresContext* presContext = frame->PresContext(); - nsIPresShell* presShell = presContext->GetPresShell(); + nsIPresShell* presShell = presContext->PresShell(); nsRootPresContext* rootPresContext = presContext->GetRootPresContext(); NotifySubDocInvalidationFunc computeInvalidFunc = @@ -1646,10 +1646,7 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB BuildContainerLayerFor(aBuilder, layerManager, frame, nullptr, this, containerParameters, nullptr); - nsIDocument* document = nullptr; - if (presShell) { - document = presShell->GetDocument(); - } + nsIDocument* document = presShell->GetDocument(); if (!root) { layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);