mirror of
https://github.com/Basilisk-Development-Team/UXP-mirror.git
synced 2026-11-04 17:00:06 -05:00
Compare commits
34
Commits
RC_20260912
...
release
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b18be917d | ||
|
|
037cc5f614 | ||
|
|
4a68bbbd53 | ||
|
|
5762097bc2 | ||
|
|
e86caa48f5 | ||
|
|
612a8991df | ||
|
|
5c991eadc0 | ||
|
|
38e09d2368 | ||
|
|
8fea2ad823 | ||
|
|
c4339b14dc | ||
|
|
bae7a6ec8f | ||
|
|
d42616bb12 | ||
|
|
5f290d1097 | ||
|
|
a0b10f4375 | ||
|
|
1d7ba07673 | ||
|
|
f51bc72a14 | ||
|
|
e8644b494f | ||
|
|
0acf5cb1a1 | ||
|
|
f2d027343a | ||
|
|
eaf2a4ac4b | ||
|
|
aa6a599aa4 | ||
|
|
f385a5e06c | ||
|
|
559e0dbdc5 | ||
|
|
e59e88bfd5 | ||
|
|
7e3d96909f | ||
|
|
d294c31da1 | ||
|
|
d699633e82 | ||
|
|
7079db6476 | ||
|
|
e938cb015b | ||
|
|
15893d9ebc | ||
|
|
0f45bf39af | ||
|
|
23398d176b | ||
|
|
7809ecf653 | ||
|
|
43d19989d4 |
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+969
-295
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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! */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
+2772
-3809
File diff suppressed because it is too large.
Load diff
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -327,7 +327,7 @@ public:
|
||||
|
||||
nsTArray<PendingServiceInfo> mPendingServices;
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
private:
|
||||
~nsComponentManagerImpl();
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user