Compare commits

...
34 Commits
Author SHA1 Message Date
Moonchild 6b18be917d [js] Avoid OOB array copy. 2026-10-26 10:17:14 +01:00
Moonchild 037cc5f614 [NSS] Remove emSign roots (eMudhra compromise)
This brings our certdata in line with NSS 3.131.1
2026-10-24 14:17:27 +02:00
Moonchild 4a68bbbd53 [NSS] Update certdata to Sept 2026 status. 2026-10-24 14:17:13 +02:00
Moonchild 5762097bc2 Update SQLite lib to 3.54.1 2026-10-24 14:16:54 +02:00
Moonchild e86caa48f5 [XPCOM] Lock during nsComponentManager memory reports 2026-10-24 14:15:59 +02:00
Moonchild 612a8991df Update executable extensions list 2026-10-24 14:15:54 +02:00
Moonchild 5c991eadc0 Clarify lowercase policy in extension list for possible future maintainers of the list.
This is added in light of common mixed-case spelling for some contemporary
file extensions. Withough explicitly mentioning the casing policy this could lead
to errors if new extensions get added by maintainers.
2026-10-24 14:15:49 +02:00
Moonchild 38e09d2368 Update executable extensions list 2026-10-24 14:15:43 +02:00
Moonchild 8fea2ad823 [layout] Ensure frame table is valid after adjusting selection. 2026-10-24 14:15:37 +02:00
Moonchild c4339b14dc Use strong ref for animation mutation observer batch targets.
We can control the scope of AnimationMutationBatch for each animation
function. However, this is tedious and prone to being overlooked, so we
use nsCOMPtr to hold the targets instead.

This is a devtool feature (ChromeOnly) so it shouldn't have much impact
on the performance for the common use case.
2026-10-24 14:15:31 +02:00
Moonchild bae7a6ec8f [gfx] Prevent feTile rect calculations from overflowing 2026-10-24 14:15:25 +02:00
Moonchild d42616bb12 Remove the URI_LOADABLE_BY_ANYONE flag from the jar: protocol. 2026-10-24 14:15:04 +02:00
Moonchild 5f290d1097 Disable :has() pseudo on release by default
Since we don't have a solution for the performance issues for this yet,
and the perf issues are a serious QoL problem for all users on many sites.
2026-10-04 02:23:25 +02:00
Moonchild a0b10f4375 Issue #3197 - Make strict subtree form element checking preffed.
Resolves #3197
2026-10-03 21:22:00 +02:00
Moonchild 1d7ba07673 Issue #3196 - Handle BigInt objects in js::Unbox()
Resolves #3196
2026-10-03 21:21:53 +02:00
Moonchild f51bc72a14 [DOM] Don't leak elements with animated classes. 2026-10-03 21:21:46 +02:00
Moonchild e8644b494f [network] Prevent NTLM login attempt with empty credentials. 2026-10-03 21:21:38 +02:00
Andrew McCreight 0acf5cb1a1 Bug 2068346 - Unwrap a copy in GetJSValFromKeyPathString. 2026-10-03 21:21:31 +02:00
Moonchild f2d027343a Backport an upstream libvpx VP8 fix 2026-10-03 21:21:26 +02:00
Moonchild eaf2a4ac4b Minor formatting cleanup. 2026-10-03 21:21:16 +02:00
Moonchild aa6a599aa4 [network] Modernize MIME header parsing module.
- Use nsCStrings instead of char*
- More thorough checking for NUL characters
- Use the more modern utility includes
2026-10-03 21:21:05 +02:00
Moonchild f385a5e06c No issue - Add sanity check to JSScript::traceChildren
This verifies if objects to be traced are still valid, to prevent
GC barriers on dead/released objects. This should avoid some crashes.
2026-10-03 21:20:50 +02:00
Moonchild 559e0dbdc5 Issue #3192 - Add null check on group_ for isSingleton()
The value being monitored is a valid object, but its associated
ObjectGroup might have become invalid.

Resolves #3192
2026-10-03 21:20:42 +02:00
Moonchild e59e88bfd5 [DOM] Fix XSLT children processing.
This was an off-by-1 placement resulting in XSLT children processing
not working.
2026-09-21 11:26:39 +02:00
Moonchild 7e3d96909f [js] Fix issues in WeakRef deref() leading to crashes.
On rare occasions, reloading busy sites with WeakRef API usage would cause
CTDs. Digging into this it seems because of early teardown and/or dangling
pointers on WeakRef objects, causing `deref()` to crash.

This adds some null checks and avoids barriers on potentially invalid
WeakRef objects in the `deref()` implementation.
2026-09-19 14:51:09 +02:00
Maurice Dauer d294c31da1 Bug 2058069 - Compare formats before adopting a managed surface in Image9. 2026-09-19 14:51:02 +02:00
Jan-Niklas Jaeschke d699633e82 Bug 2058417 - Use the normalized format consistently in DataTransfer::SetDataAtInternal. 2026-09-19 14:50:55 +02:00
Moonchild 7079db6476 [embedding] Fix ChromeTooltipListener. 2026-09-19 14:50:48 +02:00
Moonchild e938cb015b [widget] Hold a reference to nsDataObj in callbacks.
As the comment in `WaitForCompletion` explains, it does a SpinEventLoopUntil
as part of CStream callbacks, and the CStream can be unregistered during that,
which is fine, but we need to hold a strong reference first since we use
the object afterward.  The reference in `WaitForCompletion` is technically
redundant but makes future uses safer and admits a comment that future clients
may actually heed.

This also adds an unrelated missing null check while we're here.
2026-09-19 14:50:42 +02:00
Moonchild 15893d9ebc [DOM] Bail on unexpected structural errors. 2026-09-19 14:50:35 +02:00
Moonchild 0f45bf39af [network] NTLM should always prompt for credentials if the host is not in trusted-uris. 2026-09-19 14:50:29 +02:00
Moonchild 23398d176b [a11y] Uncache subtree of hidden accessible when it is coalesced. 2026-09-19 14:50:23 +02:00
Basilisk-Dev 7809ecf653 Issue #3176 - Fix excessive :has() restyles when typing
Avoid restyling entire parent subtrees for changes that only affect a
child or sibling selector. Skip unrelated class changes, cache selector
dependencies, and avoid searching sibling subtrees that cannot match.

This improves typing performance on Gitea.
2026-09-19 14:49:44 +02:00
Basilisk-Dev 43d19989d4 No Issue - Fix out-of-range float to int32 conversions
Check the range before casting to int32_t. The old code had undefined
behavior that caused incorrect JavaScript numbers on LoongArch and broke
Anubis challenges.
2026-09-19 14:49:37 +02:00
42 changed files with 21736 additions and 13408 deletions

No files matched your search

@@ -276,6 +276,7 @@ NotificationController::DropMutationEvent(AccTreeMutationEvent* aEvent)
MOZ_ASSERT(hideEvent);
if (hideEvent->NeedsShutdown()) {
mDocument->UncacheChildrenInSubtree(aEvent->GetAccessible());
mDocument->ShutdownChildrenInSubtree(aEvent->GetAccessible());
}
}
+2
View File
@@ -1307,6 +1307,8 @@ DocAccessible::UnbindFromDocument(Accessible* aAccessible)
{
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
"Unbinding the unbound accessible!");
MOZ_ASSERT(!mARIAOwnsHash.Contains(aAccessible),
"Container still lingering in mARIAOwnsHash");
// Fire focus event on accessible having DOM focus if active item was removed
// from the tree.
+17230 -8908
View File
File diff suppressed because it is too large. Load diff
+969 -295
View File
File diff suppressed because it is too large. Load diff
+19 -6
View File
@@ -695,7 +695,7 @@ nsContentList::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
!MayContainRelevantNodes(aContainer) ||
(!aFirstNewContent->HasChildren() &&
!aFirstNewContent->GetNextSibling() &&
!MatchSelf(aFirstNewContent))) {
!MatchSelf<MatchSelfMode::Insertion>(aFirstNewContent))) {
return;
}
@@ -731,7 +731,7 @@ nsContentList::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
// The new stuff is somewhere in the middle of our list; check
// whether we need to invalidate
for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
if (MatchSelf(cur)) {
if (MatchSelf<MatchSelfMode::Insertion>(cur)) {
// Uh-oh. We're gonna have to add elements into the middle
// of our list. That's not worth the effort.
SetDirty();
@@ -788,7 +788,7 @@ nsContentList::ContentInserted(nsIDocument *aDocument,
if (mState != LIST_DIRTY &&
MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
MatchSelf(aChild)) {
MatchSelf<MatchSelfMode::Insertion>(aChild)) {
SetDirty();
}
@@ -808,7 +808,7 @@ nsContentList::ContentRemoved(nsIDocument *aDocument,
if (mState != LIST_DIRTY &&
MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
MatchSelf(aChild)) {
MatchSelf<MatchSelfMode::Removal>(aChild)) {
SetDirty();
}
@@ -853,6 +853,7 @@ nsContentList::Match(Element *aElement)
ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
}
template <nsContentList::MatchSelfMode Mode>
bool
nsContentList::MatchSelf(nsIContent *aContent)
{
@@ -864,8 +865,20 @@ nsContentList::MatchSelf(nsIContent *aContent)
return false;
}
if (Match(aContent->AsElement()))
auto matches = [&](Element* aElement) {
if (Match(aElement)) {
return true;
}
if constexpr (Mode == MatchSelfMode::Removal) {
return mFunc == nsContentUtils::MatchClassNames &&
aElement->IsSVGElement() && aElement->MayHaveClass();
}
return false;
};
if (matches(aContent->AsElement())) {
return true;
}
if (!mDeep)
return false;
@@ -873,7 +886,7 @@ nsContentList::MatchSelf(nsIContent *aContent)
for (nsIContent* cur = aContent->GetFirstChild();
cur;
cur = cur->GetNextNode(aContent)) {
if (cur->IsElement() && Match(cur->AsElement())) {
if (cur->IsElement() && matches(cur->AsElement())) {
return true;
}
}
+10
View File
@@ -367,6 +367,15 @@ protected:
* @return whether we match
*/
bool Match(mozilla::dom::Element *aElement);
enum class MatchSelfMode : uint8_t {
Insertion,
// Also accept SVG elements that may have a class. SMIL can change their
// class without notification, so they can be in the list without
// matching now. Needed when aContent leaves the subtree.
Removal,
};
/**
* See if anything in the subtree rooted at aContent, including
* aContent itself, matches our criterion.
@@ -374,6 +383,7 @@ protected:
* @param aContent the root of the subtree to match against
* @return whether we match something in the tree rooted at aContent
*/
template <MatchSelfMode Mode>
bool MatchSelf(nsIContent *aContent);
/**
+4 -3
View File
@@ -2073,6 +2073,10 @@ public:
aClasses);
}
static bool MatchClassNames(mozilla::dom::Element* aElement,
int32_t aNamespaceID,
nsIAtom* aAtom, void* aData);
/**
* Returns a presshell for this document, if there is one. This will be
* aDoc's direct presshell if there is one, otherwise we'll look at all
@@ -3012,9 +3016,6 @@ private:
static void DropFragmentParsers();
static bool MatchClassNames(mozilla::dom::Element* aElement,
int32_t aNamespaceID,
nsIAtom* aAtom, void* aData);
static void DestroyClassNameArray(void* aData);
static void* AllocClassMatchingInfo(nsINode* aRootNode,
const nsString* aClasses);
+1 -1
View File
@@ -921,7 +921,7 @@ private:
// List of nodes referred to by mEntryTable so we can sort them
// For a specific pseudo element, we use its parent element as the
// batch target, so they will be put in the same EntryArray.
nsTArray<nsINode*> mBatchTargets;
nsTArray<nsCOMPtr<nsINode>> mBatchTargets;
};
inline
+6 -3
View File
@@ -682,16 +682,19 @@ DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
nsAutoString format;
GetRealFormat(aFormat, format);
// Don't allow the custom type to be assigned.
if (aFormat.EqualsLiteral(kCustomTypesMime)) {
if (format.EqualsLiteral(kCustomTypesMime)) {
return NS_ERROR_TYPE_ERR;
}
if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
if (!PrincipalMaySetData(format, aData, aSubjectPrincipal)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
return SetDataWithPrincipal(format, aData, aIndex, aSubjectPrincipal);
}
void
+3 -1
View File
@@ -138,7 +138,9 @@ GetJSValFromKeyPathString(JSContext* aCx,
// only through getters but we still want to support them for key
// extraction. So they need to be handled manually.
Blob* blob;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
// UNWRAP_OBJECT calls might mutate this.
JS::Rooted<JSObject*> blobObj(aCx, obj);
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &blobObj, blob))) {
if (token.EqualsLiteral("size")) {
ErrorResult rv;
uint64_t size = blob->GetSize(rv);
+7 -1
View File
@@ -294,8 +294,8 @@ nsXMLContentSink::DidBuildModel(bool aTerminated)
}
}
mDocumentChildren.Clear();
mXSLTProcessor->SetSourceContentModel(mDocument, mDocumentChildren);
mDocumentChildren.Clear();
// Since the processor now holds a reference to us we drop our reference
// to it to avoid owning cycles
mXSLTProcessor = nullptr;
@@ -883,6 +883,9 @@ nsXMLContentSink::SetDocElement(int32_t aNameSpaceID,
for (nsIContent* child : mDocumentChildren) {
if (MOZ_UNLIKELY(child->GetParentNode())) {
child->Remove();
if (MOZ_UNLIKELY(child->GetParentNode())) {
return false;
}
}
mDocument->AppendChildTo(child, false);
}
@@ -996,6 +999,9 @@ nsXMLContentSink::HandleStartElement(const char16_t *aName,
if (MOZ_UNLIKELY(content->GetParentNode())) {
content->Remove();
if (MOZ_UNLIKELY(content->GetParentNode())) {
return NS_ERROR_UNEXPECTED;
}
}
parent->AppendChildTo(content, false);
}
+5 -2
View File
@@ -1082,6 +1082,9 @@ ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
ChromeTooltipListener::~ChromeTooltipListener()
{
if (mTooltipTimer) {
mTooltipTimer->Cancel();
}
}
// Hook up things to the chrome like context menus and tooltips, if the chrome
@@ -1329,7 +1332,7 @@ void
ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
void* aChromeTooltipListener)
{
auto self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
RefPtr<ChromeTooltipListener> self = static_cast<ChromeTooltipListener*>(aChromeTooltipListener);
if (self && self->mPossibleTooltipNode) {
// The actual coordinates we want to put the tooltip at are relative to the
// toplevel docshell of our mWebBrowser. We know what the screen
@@ -1374,7 +1377,7 @@ ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
self->mPossibleTooltipNode, getter_Copies(tooltipText),
getter_Copies(directionText), &textFound);
if (textFound) {
if (self->mPossibleTooltipNode && textFound) {
nsString tipText(tooltipText);
nsString dirText(directionText);
LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset();
+15 -6
View File
@@ -1554,8 +1554,7 @@ FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
const IntRect &aSourceRect)
{
MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y),
int32_t(aSourceRect.width), int32_t(aSourceRect.height));
mSourceRect = aSourceRect;
Invalidate();
}
@@ -1595,14 +1594,24 @@ FilterNodeTileSoftware::Render(const IntRect& aRect)
typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
InputMap inputs;
auto IntRectToInt64Rect = [](const IntRect& aRect) {
return RectTyped<UnknownUnits, int64_t>(aRect.X(), aRect.Y(), aRect.Width(),
aRect.Height());
};
IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
IntPoint sourceToDestOffset(ix * mSourceRect.width,
iy * mSourceRect.height);
IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
IntRect srcRect = destRect - sourceToDestOffset;
auto sourceToDestOffset = PointTyped<UnknownUnits, int64_t>(
ix * mSourceRect.Width(), iy * mSourceRect.Height());
auto destRect64 = IntRectToInt64Rect(aRect).Intersect(
IntRectToInt64Rect(mSourceRect) + sourceToDestOffset);
auto srcRect64 = destRect64 - sourceToDestOffset;
IntRect destRect, srcRect;
if (!destRect64.ToIntRect(&destRect) || !srcRect64.ToIntRect(&srcRect)) {
return nullptr;
}
if (srcRect.IsEmpty()) {
continue;
}
@@ -358,7 +358,8 @@ gl::Error Image9::setManagedSurface(IDirect3DSurface9 *surface)
surface->GetDesc(&desc);
ASSERT(desc.Pool == D3DPOOL_MANAGED);
if ((GLsizei)desc.Width == mWidth && (GLsizei)desc.Height == mHeight)
if ((GLsizei)desc.Width == mWidth && (GLsizei)desc.Height == mHeight &&
desc.Format == mD3DFormat)
{
if (mSurface)
{
+8 -2
View File
@@ -69,7 +69,11 @@ WeakRef_deref_impl(JSContext* cx, const CallArgs& args)
}
if (data->isObject()) {
JSObject* target = data->objectTarget.get();
// Use unbarrieredGet() to avoid barriers on potentially invalid memory.
JSObject* target = data->objectTarget.unbarrieredGet();
// Ensure pointer is not null.
// During WeakRef deref, if the target was collected, it will be nullptr.
if (target) {
RootedValue kept(cx, ObjectValue(*target));
if (!cx->runtime()->addWeakRefKeptObject(cx, kept))
@@ -80,7 +84,9 @@ WeakRef_deref_impl(JSContext* cx, const CallArgs& args)
}
} else {
MOZ_ASSERT(data->isSymbol());
JS::Symbol* target = data->symbolTarget.get();
JS::Symbol* target = data->symbolTarget.unbarrieredGet();
// Ensure pointer is not null.
if (target) {
RootedValue kept(cx, SymbolValue(target));
if (!cx->runtime()->addWeakRefKeptObject(cx, kept))
+22 -2
View File
@@ -902,8 +902,28 @@ namespace js {
// scheduledForDestruction will be set on the compartment, which will cause
// some extra GC activity to try to free the compartment.
template<typename T> inline void SetMaybeAliveFlag(T* thing) {}
template<> inline void SetMaybeAliveFlag(JSObject* thing) {thing->compartment()->maybeAlive = true;}
template<> inline void SetMaybeAliveFlag(JSScript* thing) {thing->compartment()->maybeAlive = true;}
template<> inline void SetMaybeAliveFlag(JSObject* thing) {
// During GC marking of WeakRefs, 'thing' might be an invalid/dangling
// pointer. Verify compartment is accessible.
if (MOZ_LIKELY(thing)) {
JSCompartment* comp = thing->compartment();
if (MOZ_LIKELY(comp)) {
comp->maybeAlive = true;
}
}
}
template<> inline void SetMaybeAliveFlag(JSScript* thing) {
// During GC marking of WeakRefs, 'thing' might be an invalid/dangling
// pointer. Verify compartment is accessible.
if (MOZ_LIKELY(thing)) {
JSCompartment* comp = thing->compartment();
if (MOZ_LIKELY(comp)) {
comp->maybeAlive = true;
}
}
}
inline js::Handle<js::GlobalObject*>
ExclusiveContext::global() const
+2
View File
@@ -3940,6 +3940,8 @@ js::Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
vp.setString(obj->as<StringObject>().unbox());
else if (obj->is<DateObject>())
vp.set(obj->as<DateObject>().UTCTime());
else if (obj->is<BigIntObject>())
vp.setBigInt(obj->as<BigIntObject>().unbox());
else
vp.setUndefined();
+3 -7
View File
@@ -158,6 +158,8 @@ class JSObject : public js::gc::Cell
* object will have its group constructed lazily as needed by analysis.
*/
bool isSingleton() const {
// In some cases, group_ might be null.
if (!group_) return false;
return group_->singleton();
}
@@ -895,10 +897,6 @@ GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index,
* try. Long story short, if you just want bog-standard assignment, pass
* `ObjectValue(*obj)` as receiver. Or better, use one of the signatures that
* doesn't have a receiver parameter.
*
* Callers pass obj != receiver e.g. when a proxy is involved, obj is the
* proxy's target, and the proxy is using SetProperty to finish an assignment
* that started out as `receiver[id] = v`, by delegating it to obj.
*/
inline bool
SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
@@ -1269,9 +1267,7 @@ FromPropertyDescriptorToObject(JSContext* cx, Handle<JS::PropertyDescriptor> des
extern bool
IsDelegate(JSContext* cx, HandleObject obj, const Value& v, bool* result);
// obj is a JSObject*, but we root it immediately up front. We do it
// that way because we need a Rooted temporary in this method anyway.
extern bool
bool
IsDelegateOfObject(JSContext* cx, HandleObject protoObj, JSObject* obj, bool* result);
/* Wrap boolean, number or string as Boolean, Number or String object. */
+3 -1
View File
@@ -3954,7 +3954,9 @@ JSScript::traceChildren(JSTracer* trc)
if (hasObjects()) {
ObjectArray* objarray = objects();
TraceRange(trc, objarray->length, objarray->vector, "objects");
if (objarray && objarray->length > 0 && objarray->vector) {
TraceRange(trc, objarray->length, objarray->vector, "objects");
}
}
MOZ_ASSERT_IF(sourceObject(), MaybeForwarded(sourceObject())->compartment() == compartment());
+9 -7
View File
@@ -1373,16 +1373,18 @@ intrinsic_MoveTypedArrayElements(JSContext* cx, unsigned argc, Value* vp)
MOZ_ASSERT((UINT32_MAX >> ElementShift) >= count);
uint32_t byteSize = count << ElementShift;
#ifdef DEBUG
{
uint32_t viewByteLength = tarray->byteLength();
MOZ_ASSERT(byteSize <= viewByteLength);
MOZ_ASSERT(byteDest < viewByteLength);
MOZ_ASSERT(byteSrc < viewByteLength);
MOZ_ASSERT(byteDest <= viewByteLength - byteSize);
MOZ_ASSERT(byteSrc <= viewByteLength - byteSize);
if (byteSize > viewByteLength ||
byteDest >= viewByteLength ||
byteSrc >= viewByteLength ||
byteDest > viewByteLength - byteSize ||
byteSrc > viewByteLength - byteSize) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
return false;
}
}
#endif
SharedMem<uint8_t*> data = tarray->viewDataEither().cast<uint8_t*>();
jit::AtomicOperations::memmoveSafeWhenRacy(data + byteDest, data + byteSrc, byteSize);
+59 -38
View File
@@ -282,9 +282,12 @@ RestyleManager::AttributeWillChange(Element* aElement,
aNewValue,
rsdata);
PostRestyleEvent(aElement, rshint, nsChangeHint(0), &rsdata);
// Inspect the old classes before they are replaced. AttributeChanged does
// not always receive an old value, so it cannot detect removals by itself.
RestyleForHasPseudoClassChange(aElement, EventStates(), aAttribute);
// Class values are preparsed by Element before this notification. Compare
// membership now: AttributeChanged need not receive the previous value.
if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::_class) {
RestyleForHasPseudoClassChange(aElement, EventStates(), aAttribute,
aNewValue, true);
}
}
// Forwarded nsIMutationObserver method, to handle restyling (and
@@ -376,44 +379,68 @@ RestyleManager::AttributeChanged(Element* aElement,
aOldValue,
rsdata);
PostRestyleEvent(aElement, rshint, hint, &rsdata);
RestyleForHasPseudoClassChange(aElement, EventStates(), aAttribute);
if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::_class) {
RestyleForHasPseudoClassChange(aElement, EventStates(), aAttribute);
}
}
bool
void
RestyleManager::RestyleForHasPseudoClassChange(nsINode* aNode,
EventStates aStateMask,
nsIAtom* aAttribute)
nsIAtom* aAttribute,
const nsAttrValue* aNewClasses,
bool aCompareClasses)
{
MOZ_ASSERT(!aAttribute || aNode->IsElement());
Element* affectedRoot = nullptr;
if (!aNode->OwnerDoc()->GetProperty(nsGkAtoms::hasSelectorDependency)) {
return;
}
using Dependency = nsCSSRuleUtils::HasSelectorDependency;
auto restyleAnchor = [&](Element* aAnchor, Dependency* aDependency) {
nsRestyleHint hint = eRestyle_Subtree;
if (aDependency->mRestyleLaterSiblings) {
hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
}
PostRestyleEvent(aAnchor, hint, nsChangeHint(0));
};
for (nsINode* node = aNode; node; node = node->GetParentNode()) {
auto* dependency = static_cast<nsCSSRuleUtils::HasSelectorDependency*>(
auto* dependency = static_cast<Dependency*>(
node->GetProperty(nsGkAtoms::hasSelectorDependency));
if (!dependency ||
(!aStateMask.IsEmpty() &&
!dependency->mStates.HasAtLeastOneOfStates(aStateMask)) ||
(aAttribute && !dependency->MightDependOnAttribute(aNode->AsElement(),
aAttribute))) {
if (!dependency) {
continue;
}
if (node->IsElement()) {
affectedRoot = node->AsElement();
} else if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
affectedRoot = shadow->GetHost();
if (node->IsElement() && dependency->MightDependOnChange(
node->AsElement(), aNode, aStateMask, aAttribute, aNewClasses,
aCompareClasses, false)) {
restyleAnchor(node->AsElement(), dependency);
}
if (!dependency->MightAffectSiblingAnchor(
node, aNode, aStateMask, aAttribute, aNewClasses, aCompareClasses)) {
continue;
}
// The parent is an index of sibling anchors, not itself an anchor. Keep
// unrelated subtrees out of the restyle, including mutations deep inside
// a sibling whose :has() argument only inspects the sibling element.
bool nodeIsFollowingSibling = true;
for (nsIContent* child = node->GetFirstChild(); child;
child = child->GetNextSibling()) {
if (child == aNode) {
nodeIsFollowingSibling = false;
}
if (!child->IsElement()) {
continue;
}
auto* siblingDependency = static_cast<Dependency*>(
child->GetProperty(nsGkAtoms::hasSelectorDependency));
if (siblingDependency && siblingDependency->MightDependOnChange(
child->AsElement(), aNode, aStateMask, aAttribute, aNewClasses,
aCompareClasses, true, nodeIsFollowingSibling)) {
restyleAnchor(child->AsElement(), siblingDependency);
}
}
}
if (!affectedRoot) {
return false;
}
// The anchor can occur to the left of an outer sibling combinator, as in
// .anchor:has(.child) + .result. Include those siblings without restyling
// the parent and preceding siblings for descendant-only dependencies.
PostRestyleEvent(affectedRoot,
nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
nsChangeHint(0));
return true;
}
/* static */ uint64_t
@@ -442,9 +469,7 @@ RestyleManager::RestyleForAppend(nsIContent* aContainer,
nsIContent* aFirstNewContent)
{
// The container cannot be a document, but might be a ShadowRoot.
if (RestyleForHasPseudoClassChange(aContainer)) {
return;
}
RestyleForHasPseudoClassChange(aContainer);
if (!aContainer->IsElement()) {
return;
@@ -535,9 +560,7 @@ RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
nsIContent* aChild)
{
// The container might be a document or a ShadowRoot.
if (RestyleForHasPseudoClassChange(aContainer)) {
return;
}
RestyleForHasPseudoClassChange(aContainer);
if (!aContainer->IsElement()) {
return;
@@ -629,9 +652,7 @@ RestyleManager::ContentRemoved(nsINode* aContainer,
nsIContent* aFollowingSibling)
{
// The container might be a document or a ShadowRoot.
if (RestyleForHasPseudoClassChange(aContainer)) {
return;
}
RestyleForHasPseudoClassChange(aContainer);
if (!aContainer->IsElement()) {
return;
+7 -7
View File
@@ -314,14 +314,14 @@ public:
// the container is null, no work is needed.
void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
// Restyle a subtree containing any :has() anchor affected by a change at
// or below aNode. Returns true when such a restyle was posted.
// An empty state mask denotes a DOM/attribute mutation. Attribute changes
// are checked both before and after updating the element, so class removals
// remain observable even when AttributeChanged has no old value.
bool RestyleForHasPseudoClassChange(nsINode* aNode,
// Restyle affected :has() anchors, retaining normal mutation invalidation.
// With no state/attribute, aNode is the container of a content mutation.
// Class changes are compared before mutation, while both values are known.
void RestyleForHasPseudoClassChange(nsINode* aNode,
EventStates aStateMask = EventStates(),
nsIAtom* aAttribute = nullptr);
nsIAtom* aAttribute = nullptr,
const nsAttrValue* aNewClasses = nullptr,
bool aCompareClasses = false);
// Process any pending restyles. This should be called after
// CreateNeededFrames.
+40 -11
View File
@@ -2166,20 +2166,20 @@ nsFrameSelection::CommonPageMove(bool aForward,
//get the frame from the scrollable view
nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
if (!scrolledFrame) [[unlikely]] {
if MOZ_UNLIKELY(!scrolledFrame) {
return;
}
// find out where the caret is.
// we should know mDesiredPos value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
nsISelection* domSel = GetSelection(SelectionType::eNormal);
if (!domSel) [[unlikely]] {
if MOZ_UNLIKELY(!domSel) {
return;
}
nsRect caretPos;
nsIFrame* caretFrame = nsCaret::GetGeometry(domSel, &caretPos);
if (!caretFrame) [[unlikely]] {
if MOZ_UNLIKELY(!caretFrame) {
return;
}
@@ -2200,7 +2200,7 @@ nsFrameSelection::CommonPageMove(bool aForward,
desiredPoint.y = caretPos.y + caretPos.height/2;
nsIFrame::ContentOffsets offsets = scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
if (!offsets.content) [[unlikely]] {
if MOZ_UNLIKELY(!offsets.content) {
return;
}
@@ -2846,7 +2846,12 @@ nsFrameSelection::UnselectCells(nsIContent *aTableContent,
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
// This is needed as an nsTableWrapperFrame as we use this to know we can
// static cast tableFrame to nsTableWrapperFrame.
nsTableWrapperFrame* tableWrapperFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
// Declared as weak frame, as the frame may be freed during a call to a listener.
nsWeakFrame tableFrame(tableWrapperFrame);
if (!tableFrame)
return NS_ERROR_FAILURE;
@@ -2878,6 +2883,9 @@ nsFrameSelection::UnselectCells(nsIContent *aTableContent,
curColIndex < minColIndex || curColIndex > maxColIndex) {
mDomSelections[index]->RemoveRange(range);
if (!tableFrame.IsAlive()) {
return NS_OK;
}
// Since we've removed the range, decrement pointer to next range
mSelectedCellIndex--;
}
@@ -2885,21 +2893,30 @@ nsFrameSelection::UnselectCells(nsIContent *aTableContent,
} else {
// Remove cell from selection if it belongs to the given cells range or
// it is spanned onto the cells range.
MOZ_ASSERT(tableFrame.IsAlive());
// In general, static casting to another table type is not a good idea,
// but it is valid here due to the earlier do_QueryFrame() that
// established that tableFrame is an nsTableWrapperFrame*.
auto tempTableFrame = static_cast<nsTableWrapperFrame*>(tableFrame.GetFrame());
nsTableCellFrame* cellFrame =
tableFrame->GetCellFrameAt(curRowIndex, curColIndex);
tempTableFrame->GetCellFrameAt(curRowIndex, curColIndex);
uint32_t origRowIndex = cellFrame->RowIndex();
uint32_t origColIndex = cellFrame->ColIndex();
uint32_t actualRowSpan =
tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
tempTableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
uint32_t actualColSpan =
tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
tempTableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
if (origRowIndex <= static_cast<uint32_t>(maxRowIndex) && maxRowIndex >= 0 &&
origRowIndex + actualRowSpan - 1 >= static_cast<uint32_t>(minRowIndex) &&
origColIndex <= static_cast<uint32_t>(maxColIndex) && maxColIndex >= 0 &&
origColIndex + actualColSpan - 1 >= static_cast<uint32_t>(minColIndex)) {
mDomSelections[index]->RemoveRange(range);
if (!tableFrame.IsAlive()) {
return NS_OK;
}
// Since we've removed the range, decrement pointer to next range
mSelectedCellIndex--;
}
@@ -2925,7 +2942,12 @@ nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
nsTableWrapperFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
// This is needed as an nsTableWrapperFrame as we use this to know we can
// static cast tableFrame to nsTableWrapperFrame.
nsTableWrapperFrame* tableWrapperFrame = do_QueryFrame(aTableContent->GetPrimaryFrame());
// Declared as weak frame, as the frame may be freed during a call to a listener.
nsWeakFrame tableFrame(tableWrapperFrame);
if (!tableFrame) // Check that |table| is a table.
return NS_ERROR_FAILURE;
@@ -2936,7 +2958,12 @@ nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
uint32_t col = aStartColumnIndex;
while(true)
{
nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col);
MOZ_ASSERT(tableFrame.IsAlive());
// In general, static casting to another table type is not a good idea,
// but it is valid here due to the earlier do_QueryFrame() that
// established that tableFrame is an nsTableWrapperFrame*.
nsTableCellFrame* cellFrame =
static_cast<nsTableWrapperFrame*>(tableFrame.GetFrame())->GetCellFrameAt(row, col);
// Skip cells that are spanned from previous locations or are already selected
if (cellFrame) {
@@ -2944,7 +2971,9 @@ nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
uint32_t origCol = cellFrame->ColIndex();
if (origRow == row && origCol == col && !cellFrame->IsSelected()) {
result = SelectCellElement(cellFrame->GetContent());
if (NS_FAILED(result)) return result;
if (NS_FAILED(result) || !tableFrame.IsAlive()) {
return result;
}
}
}
// Done when we reach end column
+3
View File
@@ -24,6 +24,7 @@
#include "nsNameSpaceManager.h"
#include "nsXMLNameSpaceMap.h"
#include "nsCSSParser.h"
#include "nsCSSRuleUtils.h"
#include "nsCSSPseudoClasses.h"
#include "nsCSSAnonBoxes.h"
#include "nsTArray.h"
@@ -1009,6 +1010,7 @@ nsCSSSelectorList::~nsCSSSelectorList()
nsCSSSelector*
nsCSSSelectorList::AddSelector(char16_t aOperator)
{
mHasSelectorData = nullptr;
nsCSSSelector* newSel = new nsCSSSelector();
if (mSelectors) {
@@ -1026,6 +1028,7 @@ nsCSSSelectorList::AddSelector(char16_t aOperator)
void
nsCSSSelectorList::RemoveRightmostSelector()
{
mHasSelectorData = nullptr;
nsCSSSelector* current = mSelectors;
mSelectors = mSelectors->mNext;
MOZ_ASSERT(mSelectors,
+4
View File
@@ -34,6 +34,7 @@
class nsIAtom;
struct nsCSSSelectorList;
struct nsCSSHasSelectorData;
namespace mozilla {
enum class CSSPseudoClassType : uint8_t;
@@ -318,6 +319,9 @@ struct nsCSSSelectorList {
// stored as their leftmost selector. This flag keeps that implementation
// detail out of serialization and is preserved by cloning.
bool mIsRelativeSelector;
// Immutable dependency metadata, collected lazily for a :has() argument.
// Nodes retain this data independently of the stylesheet's lifetime.
RefPtr<nsCSSHasSelectorData> mHasSelectorData;
protected:
friend class inDOMUtils;
nsCSSSelectorList* Clone(bool aDeep) const;
+198 -39
View File
@@ -642,6 +642,10 @@ nsCSSRuleUtils::SelectorMatches(Element* aElement,
{
NS_PRECONDITION(!aSelector->IsPseudoElement(),
"Pseudo-element snuck into SelectorMatches?");
AutoRestore<bool> laterSiblingsRestorer(
aTreeMatchContext.mHasSelectorLaterSiblings);
aTreeMatchContext.mHasSelectorLaterSiblings |=
aSelector->mOperator == '+' || aSelector->mOperator == '~';
MOZ_ASSERT(aTreeMatchContext.mForStyling ||
!aNodeMatchContext.mIsRelevantLink,
"mIsRelevantLink should be set to false when mForStyling "
@@ -1734,7 +1738,7 @@ AddHasDependencyAtom(nsTArray<nsCOMPtr<nsIAtom>>& aAtoms, nsIAtom* aAtom)
// Collect every branch before matching, including :is() and legacy :not().
void
HasSelectorDependency::AddSelector(nsCSSSelector* aSelector)
nsCSSHasSelectorData::Branch::AddSelector(nsCSSSelector* aSelector)
{
for (nsCSSSelector* selector = aSelector; selector; selector = selector->mNext) {
if (selector->mIDList) {
@@ -1793,8 +1797,9 @@ HasSelectorDependency::AddSelector(nsCSSSelector* aSelector)
}
void
HasSelectorDependency::Merge(const HasSelectorDependency& aOther)
nsCSSHasSelectorData::Branch::Merge(const Branch& aOther)
{
MOZ_ASSERT(mCombinator == aOther.mCombinator && mIsLocal == aOther.mIsLocal);
mStates |= aOther.mStates;
mAllAttributes |= aOther.mAllAttributes;
for (nsIAtom* attr : aOther.mAttributes) {
@@ -1805,9 +1810,91 @@ HasSelectorDependency::Merge(const HasSelectorDependency& aOther)
}
}
void
HasSelectorDependency::AddSelectorData(nsCSSHasSelectorData* aData, bool aSibling)
{
auto& lastSelector = aSibling ? mLastSiblingSelector : mLastSelector;
if (lastSelector == aData) {
return;
}
auto& branches = aSibling ? mSiblingBranches : mBranches;
for (const auto& source : aData->mBranches) {
if (aSibling && !source.IsSibling()) {
continue;
}
nsCSSHasSelectorData::Branch* destination = nullptr;
for (auto& branch : branches) {
if (branch.mCombinator == source.mCombinator &&
branch.mIsLocal == source.mIsLocal) {
destination = &branch;
break;
}
}
if (destination) {
destination->Merge(source);
} else {
branches.AppendElement(source);
}
}
lastSelector = aData;
}
// A local compound cannot observe children, ancestors, or state propagated
// from descendants. Keep structural/state/complex functional arguments on the
// conservative path; plain :is()/:not() compounds are safe to narrow.
static bool
IsLocalHasCompound(nsCSSSelector* aSelector)
{
for (nsPseudoClassList* pseudo = aSelector->mPseudoClassList;
pseudo; pseudo = pseudo->mNext) {
switch (pseudo->mType) {
case CSSPseudoClassType::is:
case CSSPseudoClassType::matches:
case CSSPseudoClassType::any:
case CSSPseudoClassType::where:
case CSSPseudoClassType::mozAny:
case CSSPseudoClassType::mozAnyPrivate:
break;
default:
return false;
}
for (nsCSSSelectorList* list = pseudo->u.mSelectorList;
list; list = list->mNext) {
if (list->mSelectors &&
(list->mSelectors->mNext || !IsLocalHasCompound(list->mSelectors))) {
return false;
}
}
}
for (nsCSSSelector* negation = aSelector->mNegations;
negation; negation = negation->mNegations) {
if (negation->mNext || !IsLocalHasCompound(negation)) {
return false;
}
}
return true;
}
nsCSSHasSelectorData::nsCSSHasSelectorData(nsCSSSelectorList* aList)
{
for (nsCSSSelectorList* list = aList; list; list = list->mNext) {
Branch* branch = mBranches.AppendElement();
branch->AddSelector(list->mSelectors);
nsCSSSelector* anchor = list->mSelectors;
while (anchor->mNext) {
anchor = anchor->mNext;
}
branch->mCombinator = anchor->mOperator;
branch->mIsLocal = list->mSelectors->mNext == anchor &&
IsLocalHasCompound(list->mSelectors);
mHasSibling |= branch->IsSibling();
}
}
bool
HasSelectorDependency::MightDependOnAttribute(Element* aElement,
nsIAtom* aAttribute) const
nsCSSHasSelectorData::Branch::MightDependOnAttribute(
Element* aElement, nsIAtom* aAttribute, const nsAttrValue* aNewClasses,
bool aCompareClasses) const
{
if (mAllAttributes || mAttributes.Contains(aAttribute)) {
return true;
@@ -1816,41 +1903,112 @@ HasSelectorDependency::MightDependOnAttribute(Element* aElement,
return false;
}
const nsAttrValue* classes = aElement->GetClasses();
if (!classes) {
return false;
}
nsCaseTreatment caseTreatment =
aElement->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
? eIgnoreCase : eCaseMatters;
for (nsIAtom* cls : mClasses) {
if (classes->Contains(cls, caseTreatment)) {
bool hadClass = classes && classes->Contains(cls, caseTreatment);
if (aCompareClasses) {
bool hasClass = aNewClasses && aNewClasses->Contains(cls, caseTreatment);
if (hadClass != hasClass) {
return true;
}
} else if (hadClass) {
return true;
}
}
return false;
}
bool
HasSelectorDependency::MightDependOnChange(
Element* aAnchor, nsINode* aNode, EventStates aStateMask,
nsIAtom* aAttribute, const nsAttrValue* aNewClasses,
bool aCompareClasses, bool aSibling, bool aNodeIsFollowingSibling) const
{
bool contentChange = !aAttribute && aStateMask.IsEmpty();
for (const auto& branch : mBranches) {
if (branch.IsSibling() != aSibling ||
(!aStateMask.IsEmpty() &&
!branch.mStates.HasAtLeastOneOfStates(aStateMask)) ||
(aAttribute && !branch.MightDependOnAttribute(
aNode->AsElement(), aAttribute, aNewClasses, aCompareClasses))) {
continue;
}
if (branch.mIsLocal) {
if (aSibling) {
nsINode* parent = aAnchor->GetParentNode();
if (contentChange) {
// Changing a sibling's contents cannot affect a local compound.
if (aNode != parent) {
continue;
}
} else {
if (aNode->GetParentNode() != parent) {
continue;
}
if (branch.mCombinator == '+') {
if (aAnchor->GetNextElementSibling() != aNode) {
continue;
}
} else if (!aNodeIsFollowingSibling) {
continue;
}
}
} else if (branch.mCombinator == '>') {
if (contentChange ? aNode != aAnchor
: aNode->GetParentNode() != aAnchor) {
continue;
}
}
}
return true;
}
return false;
}
bool
HasSelectorDependency::MightAffectSiblingAnchor(
nsINode* aParent, nsINode* aNode, EventStates aStateMask,
nsIAtom* aAttribute, const nsAttrValue* aNewClasses,
bool aCompareClasses) const
{
bool contentChange = !aAttribute && aStateMask.IsEmpty();
for (const auto& branch : mSiblingBranches) {
if (!branch.IsSibling() ||
(branch.mIsLocal && (contentChange ? aNode != aParent
: aNode->GetParentNode() != aParent)) ||
(!aStateMask.IsEmpty() &&
!branch.mStates.HasAtLeastOneOfStates(aStateMask)) ||
(aAttribute && !branch.MightDependOnAttribute(
aNode->AsElement(), aAttribute, aNewClasses, aCompareClasses))) {
continue;
}
return true;
}
return false;
}
static void
DeleteHasSelectorDependency(void*, nsIAtom*, void* aValue, void*)
{
delete static_cast<HasSelectorDependency*>(aValue);
}
static void
NoteHasSelectorDependency(nsINode* aNode, const HasSelectorDependency& aDependency)
static HasSelectorDependency*
EnsureHasSelectorDependency(nsINode* aNode)
{
auto* dependency = static_cast<HasSelectorDependency*>(
aNode->GetProperty(nsGkAtoms::hasSelectorDependency));
if (dependency) {
dependency->Merge(aDependency);
return;
}
dependency = new HasSelectorDependency;
dependency->Merge(aDependency);
if (NS_FAILED(aNode->SetProperty(nsGkAtoms::hasSelectorDependency, dependency,
DeleteHasSelectorDependency))) {
delete dependency;
if (!dependency) {
dependency = new HasSelectorDependency;
if (NS_FAILED(aNode->SetProperty(nsGkAtoms::hasSelectorDependency, dependency,
DeleteHasSelectorDependency))) {
delete dependency;
return nullptr;
}
}
return dependency;
}
/* static */ bool
@@ -1863,27 +2021,22 @@ nsCSSRuleUtils::RelativeSelectorListMatches(
MOZ_ASSERT(aList);
if (aTreeMatchContext.mForStyling) {
HasSelectorDependency dependency;
HasSelectorDependency siblingDependency;
bool hasSiblingSelector = false;
for (nsCSSSelectorList* relative = aList; relative; relative = relative->mNext) {
HasSelectorDependency relativeDependency;
relativeDependency.AddSelector(relative->mSelectors);
dependency.Merge(relativeDependency);
nsCSSSelector* anchor = relative->mSelectors;
while (anchor->mNext) {
anchor = anchor->mNext;
}
if (anchor->mOperator == '+' || anchor->mOperator == '~') {
hasSiblingSelector = true;
siblingDependency.Merge(relativeDependency);
}
if (!aList->mHasSelectorData) {
aList->mHasSelectorData = new nsCSSHasSelectorData(aList);
}
NoteHasSelectorDependency(aAnchor, dependency);
// Descendant selectors cannot inspect following sibling subtrees. Only
// relative selectors starting with + or ~ need a marker on the parent.
if (hasSiblingSelector && aAnchor->GetParent()) {
NoteHasSelectorDependency(aAnchor->GetParent(), siblingDependency);
const auto& data = aList->mHasSelectorData;
// The document marker makes mutations in documents without styled :has()
// selectors a constant-time no-op. All markers use the same property type.
EnsureHasSelectorDependency(aAnchor->OwnerDoc());
if (auto* dependency = EnsureHasSelectorDependency(aAnchor)) {
dependency->AddSelectorData(data, false);
dependency->mRestyleLaterSiblings |=
aTreeMatchContext.mHasSelectorLaterSiblings;
}
if (data->mHasSibling && aAnchor->GetParent()) {
if (auto* dependency = EnsureHasSelectorDependency(aAnchor->GetParent())) {
dependency->AddSelectorData(data, true);
}
}
}
@@ -1940,6 +2093,12 @@ nsCSSRuleUtils::RelativeSelectorListMatches(
if (matchesSubtree(sibling, relative, aTreeMatchContext)) {
return true;
}
// Once + .a enters .a's descendants, later combinators cannot
// leave that subtree. + .a + .b still needs the broader search.
if (leftmost->mNext->mOperator == '+' &&
NS_IS_ANCESTOR_OPERATOR(leftmost->mOperator)) {
break;
}
}
break;
+52 -8
View File
@@ -9,7 +9,8 @@
#include "mozilla/Attributes.h"
#include "mozilla/EventStates.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RefCountType.h"
#include "mozilla/RefCountType.h"
#include "mozilla/RefCounted.h"
#include "mozilla/SheetType.h"
#include "mozilla/UniquePtr.h"
#include "nsExpirationTracker.h"
@@ -20,19 +21,62 @@
#include "nsTArray.h"
#include "StyleRule.h"
struct nsCSSRuleUtils
{
// Stored on nodes under hasSelectorDependency. Dependencies accumulate
// across matching passes, so a failed/short-circuited branch stays watched.
struct HasSelectorDependency {
// No selector or DOM pointers: nodes can safely keep this after a rule is
// removed, without keeping either the stylesheet or an anchor alive.
struct nsCSSHasSelectorData : public mozilla::RefCounted<nsCSSHasSelectorData> {
MOZ_DECLARE_REFCOUNTED_TYPENAME(nsCSSHasSelectorData)
struct Branch {
mozilla::EventStates mStates;
nsTArray<nsCOMPtr<nsIAtom>> mAttributes;
nsTArray<nsCOMPtr<nsIAtom>> mClasses;
bool mAllAttributes = false;
bool mIsLocal = false;
char16_t mCombinator = ' ';
void AddSelector(nsCSSSelector* aSelector);
void Merge(const HasSelectorDependency& aOther);
bool MightDependOnAttribute(Element* aElement, nsIAtom* aAttribute) const;
void Merge(const Branch& aOther);
bool MightDependOnAttribute(mozilla::dom::Element* aElement,
nsIAtom* aAttribute,
const nsAttrValue* aNewClasses,
bool aCompareClasses) const;
bool IsSibling() const {
return mCombinator == '+' || mCombinator == '~';
}
};
explicit nsCSSHasSelectorData(nsCSSSelectorList* aList);
nsTArray<Branch> mBranches;
bool mHasSibling = false;
};
struct nsCSSRuleUtils
{
// Stored on nodes under hasSelectorDependency. Dependencies accumulate
// across matching passes, so a failed/short-circuited branch stays watched.
struct HasSelectorDependency {
// Merge rules with the same search relationship. Retaining a reference
// to every rule ever matched would grow without bound on long-lived pages
// that replace stylesheets. Only the most recent registration is cached.
nsTArray<nsCSSHasSelectorData::Branch> mBranches;
nsTArray<nsCSSHasSelectorData::Branch> mSiblingBranches;
RefPtr<nsCSSHasSelectorData> mLastSelector;
RefPtr<nsCSSHasSelectorData> mLastSiblingSelector;
bool mRestyleLaterSiblings = false;
void AddSelectorData(nsCSSHasSelectorData* aData, bool aSibling);
bool MightDependOnChange(Element* aAnchor, nsINode* aNode,
mozilla::EventStates aStateMask,
nsIAtom* aAttribute,
const nsAttrValue* aNewClasses,
bool aCompareClasses,
bool aSibling,
bool aNodeIsFollowingSibling = false) const;
bool MightAffectSiblingAnchor(nsINode* aParent, nsINode* aNode,
mozilla::EventStates aStateMask,
nsIAtom* aAttribute,
const nsAttrValue* aNewClasses,
bool aCompareClasses) const;
};
static void Startup();
+3
View File
@@ -450,6 +450,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
, mIsTopmostScope(false)
, mForAssignedSlot(false)
, mRelativeSelectorAnchor(nullptr)
, mHasSelectorLaterSiblings(false)
{
if (aMatchVisited != eNeverMatchVisited) {
nsILoadContext* loadContext = mDocument->GetLoadContext();
@@ -472,6 +473,8 @@ struct MOZ_STACK_CLASS TreeMatchContext {
// relative selector. This is deliberately separate from mScopes: an
// explicit :scope inside :has() still refers to the API/style scope.
mozilla::dom::Element* mRelativeSelectorAnchor;
// Propagate an outer sibling combinator through :is(), :not(), etc.
bool mHasSelectorLaterSiblings;
};
struct MOZ_STACK_CLASS RuleProcessorData {
+4 -6
View File
@@ -333,12 +333,10 @@ void vp8_build_inter16x16_predictors_mb(MACROBLOCKD *x, unsigned char *dst_y,
_16x16mv.as_mv.row &= x->fullpixel_mask;
_16x16mv.as_mv.col &= x->fullpixel_mask;
if (2 * _16x16mv.as_mv.col < (x->mb_to_left_edge - (19 << 3)) ||
2 * _16x16mv.as_mv.col > x->mb_to_right_edge + (18 << 3) ||
2 * _16x16mv.as_mv.row < (x->mb_to_top_edge - (19 << 3)) ||
2 * _16x16mv.as_mv.row > x->mb_to_bottom_edge + (18 << 3)) {
return;
}
/* Rounding to full-pel can move the chroma MV outside the border tap
* window even though the luma MV was already clamped, so clamp again to
* keep the reference read in bounds and ensure dst_u/dst_v are written. */
clamp_uvmv_to_umv_border(&_16x16mv.as_mv, x);
pre_stride >>= 1;
offset = (_16x16mv.as_mv.row >> 3) * pre_stride + (_16x16mv.as_mv.col >> 3);
+6 -6
View File
@@ -379,12 +379,12 @@ template<typename T>
static MOZ_ALWAYS_INLINE bool
NumberEqualsInt32(T aValue, int32_t* aInt32)
{
/*
* XXX Casting a floating-point value that doesn't truncate to int32_t, to
* int32_t, induces undefined behavior. We should definitely fix this
* (bug 744965), but as apparently it "works" in practice, it's not a
* pressing concern now.
*/
// Check the range before casting: an out-of-range float-to-integer cast is
// undefined behavior. Use an exclusive upper bound of 2^31, since converting
// INT32_MAX to float rounds up to 2^31. This also rejects NaN and infinities.
if (!(aValue >= T(INT32_MIN) && aValue < -T(INT32_MIN))) {
return false;
}
return aValue == (*aInt32 = int32_t(aValue));
}
+5 -3
View File
@@ -111,9 +111,11 @@ nsJARProtocolHandler::GetDefaultPort(int32_t *result)
NS_IMETHODIMP
nsJARProtocolHandler::GetProtocolFlags(uint32_t *result)
{
// URI_LOADABLE_BY_ANYONE, since it's our inner URI that will matter
// anyway.
*result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE;
// A jar: URI is only ever as loadable as the inner URI it
// wraps, and security checks consult the innermost URI. Claim
// the restrictive flag here so that consumers which look at the
// jar: handler alone don't treat it as web-loadable.
*result = URI_NORELATIVE | URI_NOAUTH | URI_IS_LOCAL_FILE;
/* Although jar uris have their own concept of relative urls
it is very different from the standard behaviour, so we
have to say norelative here! */
+4 -1
View File
@@ -2573,7 +2573,7 @@ pref("layout.css.legacy-negation-pseudo.enabled", false);
pref("layout.css.is-where-pseudo.enabled", true);
// Is support for the :has() selector enabled?
pref("layout.css.has-pseudo.enabled", true);
pref("layout.css.has-pseudo.enabled", false);
// Is support for the ::slotted() selector enabled?
pref("layout.css.slotted-pseudo.enabled", true);
@@ -4516,6 +4516,9 @@ pref("html5.flushtimer.initialdelay", 120);
// Time in milliseconds between the time a network buffer is seen and the
// timer firing when the timer has already fired previously in this parse.
pref("html5.flushtimer.subsequentdelay", 120);
// Determine whether strict (standards-compliant) subtree node checking is
// enabled for forms. See UXP issue #3197
pref("html5.forms.standards_compliant", true);
// Push/Pop/Replace State prefs
pref("browser.history.maxStateObjectSize", 655360);
+208 -227
View File
@@ -3,11 +3,17 @@
* 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 "nsMIMEHeaderParamImpl.h"
#include <string.h>
#include "prmem.h"
#include "mozilla/Base64.h"
#include "mozilla/Maybe.h"
#include "mozilla/TextUtils.h"
#include "mozilla/dom/EncodingUtils.h"
#include "prprf.h"
#include "plstr.h"
#include "plbase64.h"
#include "nsCRT.h"
#include "nsMemory.h"
#include "nsTArray.h"
@@ -16,18 +22,20 @@
#include "nsIUTF8ConverterService.h"
#include "nsUConvCID.h"
#include "nsIServiceManager.h"
#include "nsMIMEHeaderParamImpl.h"
#include "nsReadableUtils.h"
#include "nsNativeCharsetUtils.h"
#include "nsError.h"
#include "nsIUnicodeDecoder.h"
#include "mozilla/dom/EncodingUtils.h"
using mozilla::dom::EncodingUtils;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
// static functions declared below are moved from mailnews/mime/src/comi18n.cpp
static char *DecodeQ(const char *, uint32_t);
static Maybe<nsCString> DecodeQ(const char *, uint32_t);
static bool Is7bitNonAsciiString(const char *, uint32_t);
static void CopyRawHeader(const char *, uint32_t, const char *, nsACString &);
static nsresult DecodeRFC2047Str(const char *, const char *, bool, nsACString&);
@@ -48,8 +56,7 @@ nsMIMEHeaderParamImpl::GetParameter(const nsACString& aHeaderVal,
const char *aParamName,
const nsACString& aFallbackCharset,
bool aTryLocaleCharset,
char **aLang, nsAString& aResult)
{
char **aLang, nsAString& aResult) {
return DoGetParameter(aHeaderVal, aParamName, MIME_FIELD_ENCODING,
aFallbackCharset, aTryLocaleCharset, aLang, aResult);
}
@@ -59,8 +66,7 @@ nsMIMEHeaderParamImpl::GetParameterHTTP(const nsACString& aHeaderVal,
const char *aParamName,
const nsACString& aFallbackCharset,
bool aTryLocaleCharset,
char **aLang, nsAString& aResult)
{
char **aLang, nsAString& aResult) {
return DoGetParameter(aHeaderVal, aParamName, HTTP_FIELD_ENCODING,
aFallbackCharset, aTryLocaleCharset, aLang, aResult);
}
@@ -94,76 +100,75 @@ nsMIMEHeaderParamImpl::DoGetParameter(const nsACString& aHeaderVal,
ParamDecoding aDecoding,
const nsACString& aFallbackCharset,
bool aTryLocaleCharset,
char **aLang, nsAString& aResult)
{
aResult.Truncate();
nsresult rv;
char **aLang, nsAString& aResult) {
aResult.Truncate();
nsresult rv;
// get parameter (decode RFC 2231/5987 when applicable, as specified by
// aDecoding (5987 being a subset of 2231) and return charset.)
nsXPIDLCString med;
nsXPIDLCString charset;
rv = DoParameterInternal(aHeaderVal, aParamName, aDecoding,
getter_Copies(charset), aLang, getter_Copies(med));
if (NS_FAILED(rv))
return rv;
// get parameter (decode RFC 2231/5987 when applicable, as specified by
// aDecoding (5987 being a subset of 2231) and return charset.)
nsXPIDLCString med;
nsXPIDLCString charset;
rv = DoParameterInternal(aHeaderVal, aParamName, aDecoding,
getter_Copies(charset), aLang, getter_Copies(med));
if (NS_FAILED(rv)) {
return rv;
}
// convert to UTF-8 after charset conversion and RFC 2047 decoding
// if necessary.
// convert to UTF-8 after charset conversion and RFC 2047 decoding
// if necessary.
nsAutoCString str1;
rv = internalDecodeParameter(med, charset.get(), nullptr, false,
// was aDecoding == MIME_FIELD_ENCODING
// see bug 875615
true,
str1);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString str1;
rv = internalDecodeParameter(med, charset.get(), nullptr, false,
// was aDecoding == MIME_FIELD_ENCODING
// see bug 875615
true,
str1);
NS_ENSURE_SUCCESS(rv, rv);
if (!aFallbackCharset.IsEmpty())
{
nsAutoCString charset;
EncodingUtils::FindEncodingForLabel(aFallbackCharset, charset);
nsAutoCString str2;
nsCOMPtr<nsIUTF8ConverterService>
cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
if (cvtUTF8 &&
NS_SUCCEEDED(cvtUTF8->ConvertStringToUTF8(str1,
PromiseFlatCString(aFallbackCharset).get(), false,
!charset.EqualsLiteral("UTF-8"),
1, str2))) {
CopyUTF8toUTF16(str2, aResult);
return NS_OK;
}
}
if (IsUTF8(str1)) {
CopyUTF8toUTF16(str1, aResult);
if (!aFallbackCharset.IsEmpty()) {
nsAutoCString charset;
EncodingUtils::FindEncodingForLabel(aFallbackCharset, charset);
nsAutoCString str2;
nsCOMPtr<nsIUTF8ConverterService> cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
if (cvtUTF8 &&
NS_SUCCEEDED(cvtUTF8->ConvertStringToUTF8(str1,
PromiseFlatCString(aFallbackCharset).get(),
false,
!charset.EqualsLiteral("UTF-8"),
1,
str2))) {
CopyUTF8toUTF16(str2, aResult);
return NS_OK;
}
}
if (aTryLocaleCharset && !NS_IsNativeUTF8())
return NS_CopyNativeToUnicode(str1, aResult);
CopyASCIItoUTF16(str1, aResult);
if (IsUTF8(str1)) {
CopyUTF8toUTF16(str1, aResult);
return NS_OK;
}
if (aTryLocaleCharset && !NS_IsNativeUTF8()) {
return NS_CopyNativeToUnicode(str1, aResult);
}
CopyASCIItoUTF16(str1, aResult);
return NS_OK;
}
// remove backslash-encoded sequences from quoted-strings
// modifies string in place, potentially shortening it
void RemoveQuotedStringEscapes(char *src)
{
char *dst = src;
// modifies aValue, potentially shortening it
void RemoveQuotedStringEscapes(nsCString& aValue) {
nsAutoCString unescaped;
unescaped.SetCapacity(aValue.Length());
for (char *c = src; *c; ++c)
{
if (c[0] == '\\' && c[1])
{
for (uint32_t i = 0; i < aValue.Length(); ++i) {
if (aValue[i] == '\\' && i + 1 < aValue.Length()) {
// skip backslash if not at end
++c;
++i;
}
*dst++ = *c;
unescaped.Append(aValue[i]);
}
*dst = 0;
aValue = std::move(unescaped);
}
// true is character is a hex digit
@@ -176,12 +181,13 @@ bool IsHexDigit(char aChar)
(c >= '0' && c <= '9');
}
// validate that a C String containing %-escapes is syntactically valid
bool IsValidPercentEscaped(const char *aValue, int32_t len)
{
for (int32_t i = 0; i < len; i++) {
// validate that a string containing %-escapes is syntactically valid
bool IsValidPercentEscaped(const nsACString& aValue) {
for (uint32_t i = 0; i < aValue.Length(); i++) {
if (aValue[i] == '%') {
if (!IsHexDigit(aValue[i + 1]) || !IsHexDigit(aValue[i + 2])) {
if (i + 2 >= aValue.Length() ||
!IsHexDigit(aValue[i + 1]) ||
!IsHexDigit(aValue[i + 2])) {
return false;
}
}
@@ -189,6 +195,21 @@ bool IsValidPercentEscaped(const char *aValue, int32_t len)
return true;
}
// A NUL anywhere in a decoded value would silently truncate it in any later
// C-string based processing. Note that this is stricter than
// ContainsTrailingCharPastNull, which tolerates a value whose tail past the
// NUL is only more NULs.
static bool ContainsNull(const nsACString& aValue) {
return aValue.Contains('\0');
}
// Percent-decode aValue in place (which happens whether or not this succeeds),
// returning false if the decoded value contains a NUL.
static bool UnescapeRejectingNull(nsCString& aValue) {
NS_UnescapeURL(aValue);
return !ContainsNull(aValue);
}
// Support for continuations (RFC 2231, Section 3)
// only a sane number supported
@@ -220,59 +241,41 @@ class Continuation {
bool wasQuotedString;
};
// combine segments into a single string, returning the allocated string
// (or nullptr) while emptying the list
char *combineContinuations(nsTArray<Continuation>& aArray)
{
// Sanity check
if (aArray.Length() == 0)
return nullptr;
// combine segments into a single string, up to the first segment that is
// missing. Returns Nothing() if that leaves nothing at all, or, with
// aContainedNull set, if percent-decoding a segment produced a NUL.
Maybe<nsCString> combineContinuations(const nsTArray<Continuation>& aArray,
bool& aContainedNull) {
nsCString result;
// Get an upper bound for the length
uint32_t length = 0;
for (uint32_t i = 0; i < aArray.Length(); i++) {
length += aArray[i].length;
}
for (const Continuation& cont : aArray) {
if (!cont.value) break;
// Allocate
char *result = (char *) moz_xmalloc(length + 1);
// Concatenate
if (result) {
*result = '\0';
for (uint32_t i = 0; i < aArray.Length(); i++) {
Continuation cont = aArray[i];
if (! cont.value) break;
char *c = result + strlen(result);
strncat(result, cont.value, cont.length);
if (cont.needsPercentDecoding) {
nsUnescape(c);
}
if (cont.wasQuotedString) {
RemoveQuotedStringEscapes(c);
nsAutoCString segment(cont.value, cont.length);
if (cont.needsPercentDecoding) {
if (!UnescapeRejectingNull(segment)) {
aContainedNull = true;
return Nothing();
}
}
// return null if empty value
if (*result == '\0') {
free(result);
result = nullptr;
if (cont.wasQuotedString) {
RemoveQuotedStringEscapes(segment);
}
} else {
// Handle OOM
NS_WARNING("Out of memory\n");
result.Append(segment);
}
return result;
// no result if empty value
if (result.IsEmpty()) {
return Nothing();
}
return Some(std::move(result));
}
// add a continuation, return false on error if segment already has been seen
bool addContinuation(nsTArray<Continuation>& aArray, uint32_t aIndex,
const char *aValue, uint32_t aLength,
bool aNeedsPercentDecoding, bool aWasQuotedString)
{
bool aNeedsPercentDecoding, bool aWasQuotedString) {
if (aIndex < aArray.Length() && aArray[aIndex].value) {
NS_WARNING("duplicate RC2231 continuation segment #\n");
return false;
@@ -299,8 +302,7 @@ bool addContinuation(nsTArray<Continuation>& aArray, uint32_t aIndex,
}
// parse a segment number; return -1 on error
int32_t parseSegmentNumber(const char *aValue, int32_t aLen)
{
int32_t parseSegmentNumber(const char *aValue, int32_t aLen) {
if (aLen < 1) {
NS_WARNING("segment number missing\n");
return -1;
@@ -332,20 +334,16 @@ int32_t parseSegmentNumber(const char *aValue, int32_t aLen)
// validate a given octet sequence for compliance with the specified
// encoding
bool IsValidOctetSequenceForCharset(nsACString& aCharset, const char *aOctets)
{
nsCOMPtr<nsIUTF8ConverterService> cvtUTF8(do_GetService
(NS_UTF8CONVERTERSERVICE_CONTRACTID));
bool IsValidOctetSequenceForCharset(nsACString& aCharset, const nsACString& aOctets) {
nsCOMPtr<nsIUTF8ConverterService> cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
if (!cvtUTF8) {
NS_WARNING("Can't get UTF8ConverterService\n");
return false;
}
nsAutoCString tmpRaw;
tmpRaw.Assign(aOctets);
nsAutoCString tmpDecoded;
nsresult rv = cvtUTF8->ConvertStringToUTF8(tmpRaw,
nsresult rv = cvtUTF8->ConvertStringToUTF8(aOctets,
PromiseFlatCString(aCharset).get(),
false, false, 1, tmpDecoded);
@@ -371,8 +369,7 @@ nsMIMEHeaderParamImpl::GetParameterInternal(const nsACString& aHeaderValue,
const char* aParamName,
char** aCharset,
char** aLang,
char** aResult)
{
char** aResult) {
return DoParameterInternal(aHeaderValue, aParamName, MIME_FIELD_ENCODING,
aCharset, aLang, aResult);
}
@@ -384,9 +381,7 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
ParamDecoding aDecoding,
char** aCharset,
char** aLang,
char** aResult)
{
char** aResult) {
if (aHeaderValue.IsEmpty() || !aResult) {
return NS_ERROR_INVALID_ARG;
}
@@ -405,8 +400,12 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
*aResult = nullptr;
if (aCharset) *aCharset = nullptr;
if (aLang) *aLang = nullptr;
if (aCharset) {
*aCharset = nullptr;
}
if (aLang) {
*aLang = nullptr;
}
nsAutoCString charset;
@@ -415,8 +414,8 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
bool acceptContinuations = true;
// skip leading white space.
for (; *str && nsCRT::IsAsciiSpace(*str); ++str)
;
for (; *str && nsCRT::IsAsciiSpace(*str); ++str) { }
const char *start = str;
// aParamName is empty. return the first (possibly) _unnamed_ 'parameter'
@@ -429,9 +428,7 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
if (str == start)
return NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY;
*aResult = (char *) nsMemory::Clone(start, (str - start) + 1);
NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
(*aResult)[str - start] = '\0'; // null-terminate
*aResult = ToNewCString(Substring(start, str - start));
return NS_OK;
}
@@ -465,9 +462,14 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
// collect results for the different algorithms (plain filename,
// RFC5987/2231-encoded filename, + continuations) separately and decide
// which to use at the end
char *caseAResult = nullptr;
char *caseBResult = nullptr;
char *caseCDResult = nullptr;
Maybe<nsCString> caseAResult;
Maybe<nsCString> caseBResult;
Maybe<nsCString> caseCDResult;
// set when percent-decoding produced a NUL, which makes us reject the
// parameter outright rather than fall back to a value the sender did not
// intend to be used
bool containedNull = false;
// collect continuation segments
nsTArray<Continuation> segments;
@@ -548,15 +550,14 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
// if the parameter spans across multiple lines we have to strip out the
// line continuation -- jht 4/29/98
nsAutoCString tempStr(valueStart, valueEnd - valueStart);
nsCString tempStr(valueStart, valueEnd - valueStart);
tempStr.StripChars("\r\n");
char *res = ToNewCString(tempStr);
NS_ENSURE_TRUE(res, NS_ERROR_OUT_OF_MEMORY);
if (isQuotedString)
RemoveQuotedStringEscapes(res);
if (isQuotedString) {
RemoveQuotedStringEscapes(tempStr);
}
caseAResult = res;
caseAResult.emplace(std::move(tempStr));
// keep going, we may find a RFC 2231/5987 encoded alternative
}
// case B, C, and D
@@ -645,19 +646,16 @@ nsMIMEHeaderParamImpl::DoParameterInternal(const nsACString& aHeaderValue,
// non-empty value part
if (rawValLength > 0) {
if (!caseBResult && caseB) {
if (!IsValidPercentEscaped(rawValStart, rawValLength)) {
nsCString rawVal(rawValStart, rawValLength);
if (!IsValidPercentEscaped(rawVal)) {
goto increment_str;
}
// allocate buffer for the raw value
char *tmpResult = (char *) nsMemory::Clone(rawValStart, rawValLength + 1);
if (!tmpResult) {
goto increment_str;
if (!UnescapeRejectingNull(rawVal)) {
containedNull = true;
break;
}
*(tmpResult + rawValLength) = 0;
nsUnescape(tmpResult);
caseBResult = tmpResult;
caseBResult.emplace(std::move(rawVal));
} else {
// caseC
bool added = addContinuation(segments, 0, rawValStart,
@@ -701,44 +699,45 @@ increment_str:
while (nsCRT::IsAsciiSpace(*str)) ++str;
}
caseCDResult = combineContinuations(segments);
if (!containedNull) {
caseCDResult = combineContinuations(segments, containedNull);
}
if (containedNull) {
// Percent-decoding produced a NUL, which would truncate the value.
return NS_ERROR_INVALID_ARG;
}
if (caseBResult && !charsetB.IsEmpty()) {
// check that the 2231/5987 result decodes properly given the
// specified character set
if (!IsValidOctetSequenceForCharset(charsetB, caseBResult))
caseBResult = nullptr;
if (!IsValidOctetSequenceForCharset(charsetB, *caseBResult)) {
caseBResult.reset();
}
}
if (caseCDResult && !charsetCD.IsEmpty()) {
// check that the 2231/5987 result decodes properly given the
// specified character set
if (!IsValidOctetSequenceForCharset(charsetCD, caseCDResult))
caseCDResult = nullptr;
if (!IsValidOctetSequenceForCharset(charsetCD, *caseCDResult)) {
caseCDResult.reset();
}
}
if (caseBResult) {
// prefer simple 5987 format over 2231 with continuations
*aResult = caseBResult;
caseBResult = nullptr;
*aResult = ToNewCString(*caseBResult);
charset.Assign(charsetB);
}
else if (caseCDResult) {
// prefer 2231/5987 with or without continuations over plain format
*aResult = caseCDResult;
caseCDResult = nullptr;
*aResult = ToNewCString(*caseCDResult);
charset.Assign(charsetCD);
}
else if (caseAResult) {
*aResult = caseAResult;
caseAResult = nullptr;
*aResult = ToNewCString(*caseAResult);
}
// free unused stuff
free(caseAResult);
free(caseBResult);
free(caseCDResult);
// if we have a result
if (*aResult) {
// then return charset and lang as well
@@ -825,23 +824,6 @@ bool IsRFC5987AttrChar(char aChar)
c == '_' || c == '`' || c == '|' || c == '~');
}
// percent-decode a value
// returns false on failure
bool PercentDecode(nsACString& aValue)
{
char *c = (char *) moz_xmalloc(aValue.Length() + 1);
if (!c) {
return false;
}
strcpy(c, PromiseFlatCString(aValue).get());
nsUnescape(c);
aValue.Assign(c);
free(c);
return true;
}
// Decode a parameter value using the encoding defined in RFC 5987
//
// charset "'" [ language ] "'" value-chars
@@ -909,8 +891,8 @@ nsMIMEHeaderParamImpl::DecodeRFC5987Param(const nsACString& aParamVal,
}
// percent-decode
if (!PercentDecode(value)) {
return NS_ERROR_OUT_OF_MEMORY;
if (!UnescapeRejectingNull(value)) {
return NS_ERROR_INVALID_ARG;
}
// return the encoding
@@ -999,52 +981,44 @@ nsMIMEHeaderParamImpl::DecodeParameter(const nsACString& aParamValue,
(0x41 <= uint8_t(c) && uint8_t(c) <= 0x46) || \
(0x61 <= uint8_t(c) && uint8_t(c) <= 0x66))
// Decode Q encoding (RFC 2047).
// Decode Q encoding (RFC 2047). Returns Nothing() on bad syntax.
// static
char *DecodeQ(const char *in, uint32_t length)
{
char *out, *dest = 0;
Maybe<nsCString> DecodeQ(const char* in, uint32_t length) {
nsCString dest;
dest.SetCapacity(length);
out = dest = (char *)PR_Calloc(length + 1, sizeof(char));
if (dest == nullptr)
return nullptr;
while (length > 0) {
unsigned c = 0;
switch (*in) {
case '=':
// check if |in| in the form of '=hh' where h is [0-9a-fA-F].
if (length < 3 || !ISHEXCHAR(in[1]) || !ISHEXCHAR(in[2]))
goto badsyntax;
PR_sscanf(in + 1, "%2X", &c);
*out++ = (char) c;
in += 3;
length -= 3;
break;
case '=':
// check if |in| in the form of '=hh' where h is [0-9a-fA-F].
if (length < 3 || !ISHEXCHAR(in[1]) || !ISHEXCHAR(in[2])) {
return Nothing();
}
PR_sscanf(in + 1, "%2X", &c);
dest.Append((char)c);
in += 3;
length -= 3;
break;
case '_':
*out++ = ' ';
in++;
length--;
break;
case '_':
dest.Append(' ');
in++;
length--;
break;
default:
if (*in & 0x80) goto badsyntax;
*out++ = *in++;
length--;
default:
if (*in & 0x80) {
return Nothing();
}
dest.Append(*in++);
length--;
}
}
*out++ = '\0';
for (out = dest; *out ; ++out) {
if (*out == '\t')
*out = ' ';
}
dest.ReplaceChar('\t', ' ');
return dest;
badsyntax:
PR_Free(dest);
return nullptr;
return Some(std::move(dest));
}
// check if input is HZ (a 7bit encoding for simplified Chinese : RFC 1842))
@@ -1154,17 +1128,25 @@ void CopyRawHeader(const char *aInput, uint32_t aLen,
nsresult DecodeQOrBase64Str(const char *aEncoded, size_t aLen, char aQOrBase64,
const char *aCharset, nsACString &aResult)
{
char *decodedText;
nsCString decodedText;
NS_ASSERTION(aQOrBase64 == 'Q' || aQOrBase64 == 'B', "Should be 'Q' or 'B'");
if(aQOrBase64 == 'Q')
decodedText = DecodeQ(aEncoded, aLen);
else if (aQOrBase64 == 'B') {
decodedText = PL_Base64Decode(aEncoded, aLen, nullptr);
if(aQOrBase64 == 'Q') {
Maybe<nsCString> decoded = DecodeQ(aEncoded, aLen);
if (!decoded) {
return NS_ERROR_INVALID_ARG;
}
decodedText = std::move(*decoded);
} else if (aQOrBase64 == 'B') {
if (NS_FAILED(
mozilla::Base64Decode(Substring(aEncoded, aLen), decodedText))) {
return NS_ERROR_INVALID_ARG;
}
} else {
return NS_ERROR_INVALID_ARG;
}
if (!decodedText) {
if (ContainsNull(decodedText)) {
// a NUL would truncate the decoded value
return NS_ERROR_INVALID_ARG;
}
@@ -1174,12 +1156,11 @@ nsresult DecodeQOrBase64Str(const char *aEncoded, size_t aLen, char aQOrBase64,
nsAutoCString utf8Text;
if (NS_SUCCEEDED(rv)) {
// skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset.
rv = cvtUTF8->ConvertStringToUTF8(nsDependentCString(decodedText),
rv = cvtUTF8->ConvertStringToUTF8(decodedText,
aCharset,
IS_7BIT_NON_ASCII_CHARSET(aCharset),
true, 1, utf8Text);
}
PR_Free(decodedText);
if (NS_FAILED(rv)) {
return rv;
}
@@ -825,6 +825,13 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
// as restartable.
mAuthChannel->ConnectionRestartable(mConnectionBased && !authAtProgress);
if (identFromURI &&
(authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)) {
LOG((" ignoring URL identity for a domain-identity scheme\n"));
ident->Clear();
identFromURI = false;
}
if (identityInvalid) {
if (entry) {
if (ident->Equals(entry->Identity())) {
+9 -1
View File
@@ -279,6 +279,7 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel,
if (PL_strcasecmp(challenge, "NTLM") == 0) {
nsCOMPtr<nsISupports> module;
mAllowDefaultCredentials = CanUseDefaultCredentials(channel, isProxyAuth);
// Check to see if we should default to our generic NTLM auth module
// through UseGenericNTLM. (We use native auth by default if the
// system provides it.) If *sessionState is non-null, we failed to
@@ -288,7 +289,7 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel,
// Check for approved default credentials hosts and proxies. If
// *continuationState is non-null, the last authentication attempt
// failed so skip default credential use.
if (!*continuationState && CanUseDefaultCredentials(channel, isProxyAuth)) {
if (!*continuationState && mAllowDefaultCredentials) {
// Try logging in with the user's default credentials. If
// successful, |identityInvalid| is false, which will trigger
// a default credentials attempt once we return.
@@ -399,6 +400,13 @@ nsHttpNTLMAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
// initial challenge
if (PL_strcasecmp(challenge, "NTLM") == 0) {
// An empty user or password makes nsAuthSSPI::Init authenticate as the
// logged-in user, which only CanUseDefaultCredentials() hosts may do.
if (mUseNative && !mAllowDefaultCredentials &&
(!*user || !*pass)) {
LOG(("Not using default credentials for an untrusted host\n"));
return NS_ERROR_ABORT;
}
// NTLM service name format is 'HTTP@host' for both http and https
nsCOMPtr<nsIURI> uri;
rv = authChannel->GetURI(getter_AddRefs(uri));
+5 -1
View File
@@ -22,7 +22,11 @@ private:
// This flag indicates whether we are using the native NTLM implementation
// or the internal one.
bool mUseNative;
bool mUseNative;
// Whether the prefs let this host use the logged-in user's identity. Set by
// ChallengeReceived, read by GenerateCredentials.
bool mAllowDefaultCredentials = false;
};
} // namespace net
+4 -1
View File
@@ -41,6 +41,7 @@
#include "nsIHTMLDocument.h"
#include "mozilla/Likely.h"
#include "nsTextNode.h"
#include "mozilla/Preferences.h"
using namespace mozilla;
@@ -690,7 +691,9 @@ nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName,
void
nsHtml5TreeOperation::SetFormElement(nsIContent* aNode, nsIContent* aParent)
{
if (aNode->SubtreeRoot() != aParent->SubtreeRoot()) {
// sFormSubtreeStrict is a pref to temporarily disable this check. See UXP #3197
bool sFormSubtreeStrict = mozilla::Preferences::GetBool("html5.forms.standards_compliant", true);
if (sFormSubtreeStrict && aNode->SubtreeRoot() != aParent->SubtreeRoot()) {
return;
}
nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aNode));
File diff suppressed because it is too large. Load diff
+16
View File
@@ -184,8 +184,18 @@ NS_IMETHODIMP nsDataObj::CStream::OnStopRequest(nsIRequest *aRequest,
// Pumps thread messages while waiting for the async listener operation to
// complete. Failing this call will fail the stream incall from Windows
// and cancel the operation.
// The method spins the event loop during operation and may unregister the
// CStream, so it holds a strong reference for its duration. Callers
// must consider that, upon return, the CStream may have been unregistered and
// destroyed, so they will need to hold a strong reference if they need it to
// exist after this.
nsresult nsDataObj::CStream::WaitForCompletion()
{
// HttpBaseChannel::ReleaseListeners, as part of
// nsIRequestObserver::OnStopRequest, may drop its reference when we spin the
// event loop.
RefPtr<CStream> keepAliveDuringWait(this);
// We are guaranteed OnStopRequest will get called, so this should be ok.
while (!mChannelRead) {
// Pump messages
@@ -233,6 +243,8 @@ STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer,
ULONG nBytesToRead,
ULONG* nBytesRead)
{
RefPtr<CStream> keepAliveDuringRead(this);
// Wait for the write into our buffer to complete via the stream listener.
// We can't respond to this by saying "call us back later".
if (NS_FAILED(WaitForCompletion()))
@@ -285,6 +297,8 @@ STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
if (statstg == nullptr)
return STG_E_INVALIDPOINTER;
RefPtr<CStream> keepAliveDuringRead(this);
if (!mChannel || NS_FAILED(WaitForCompletion()))
return E_FAIL;
@@ -299,6 +313,8 @@ STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
nsAutoCString strFileName;
nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
if (!sourceURL) return E_FAIL;
sourceURL->GetFileName(strFileName);
if (strFileName.IsEmpty())
+2 -1
View File
@@ -1776,10 +1776,11 @@ nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
size_t
nsComponentManagerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
const
{
size_t n = aMallocSizeOf(this);
SafeMutexAutoLock lock(mLock);
n += mLoaderMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf);
+1 -1
View File
@@ -327,7 +327,7 @@ public:
nsTArray<PendingServiceInfo> mPendingServices;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
private:
~nsComponentManagerImpl();
+6 -2
View File
@@ -3009,6 +3009,7 @@ nsLocalFile::IsExecutable(bool* aResult)
}
// Search for any of the set of executable extensions.
// Note: always use lower case here since we lowercase the extensions before comparing.
static const char* const executableExts[] = {
"ad",
"ade", // access project extension
@@ -3016,6 +3017,7 @@ nsLocalFile::IsExecutable(bool* aResult)
"air", // Adobe AIR installer
"app", // executable application
"application", // from bug 348763
"appcontent-ms",
"appref-ms", // ClickOnce link
"appx",
"appxbundle",
@@ -3061,11 +3063,12 @@ nsLocalFile::IsExecutable(bool* aResult)
"msc",
"msh", // Microsoft Shell
"mshxml", // Microsoft Shell
"msi",
"msi", // Standalone microsoft installer package
"msix",
"msixbundle",
"msp",
"mst",
// "msu", // Windows Update Standalone Installer. Gated by Windows on MS signatures already.
"ops", // Office Profile Settings
"pcd",
"pif",
@@ -3077,7 +3080,8 @@ nsLocalFile::IsExecutable(bool* aResult)
"scf", // Windows explorer command
"scr",
"sct",
"search-ms", // Windows Saved Search
"searchconnector-ms", // Windows Search connector installs an OpenSearch provider
"search-ms", // Windows Saved Search
"settingcontent-ms",
"shb",
"shs",