forked from upstream/UXP-mirror
Compare commits
13
Commits
master
...
stringutils
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44d17e4ee7 | ||
|
|
26cc7c3126 | ||
|
|
cea1b66ba5 | ||
|
|
948559c22c | ||
|
|
5d7a1ca5a9 | ||
|
|
613443c5eb | ||
|
|
d7884bb96c | ||
|
|
fc25f50e5f | ||
|
|
b70b3a5aa1 | ||
|
|
88ae39ed04 | ||
|
|
d893526f82 | ||
|
|
7341809f67 | ||
|
|
21743e0b7c |
No files matched your search
@@ -81,7 +81,7 @@ $(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(addprefix $(TOPOBJDIR)/
|
||||
--track install_$(subst /,_,$*).track \
|
||||
$(TOPOBJDIR)/$* \
|
||||
-DAB_CD=en-US \
|
||||
-DBOOKMARKS_INCLUDE_DIR=$(TOPSRCDIR)/browser/locales/en-US/profile \
|
||||
-DBOOKMARKS_INCLUDE_DIR=$(TOPSRCDIR)/$(MOZ_BUILD_APP)/locales/en-US/profile \
|
||||
$(ACDEFINES) \
|
||||
install_$(subst /,_,$*)
|
||||
|
||||
|
||||
@@ -444,6 +444,7 @@ GK_ATOM(handheldFriendly, "HandheldFriendly")
|
||||
GK_ATOM(handler, "handler")
|
||||
GK_ATOM(handlers, "handlers")
|
||||
GK_ATOM(HARD, "HARD")
|
||||
GK_ATOM(hasSelectorDependency, "has-selector-dependency")
|
||||
GK_ATOM(hasSameNode, "has-same-node")
|
||||
GK_ATOM(hbox, "hbox")
|
||||
GK_ATOM(head, "head")
|
||||
|
||||
+7
-1
@@ -312,7 +312,13 @@ cvt_s(SprintfState* ss, const char* s, int width, int prec, int flags)
|
||||
s = generic_null_str(s);
|
||||
|
||||
// Limit string length by precision value
|
||||
size_t slen = strnlen(s, size_t(prec));
|
||||
// We'd want to use strnlen() here, but it is not supported by all targets
|
||||
// (Most notably old OS X), so use memchr instead.
|
||||
// Replace with `size_t slen = strnlen(s, size_t(prec));` once we no longer
|
||||
// need to cater to this.
|
||||
const char* sptr = (const char*)memchr(s, 0, size_t(prec));
|
||||
size_t slen = sptr ? sptr - s : size_t(prec);
|
||||
|
||||
if (slen > INT_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <algorithm> // For std::max
|
||||
#include "mozilla/EffectSet.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "AnimationCommon.h" // For GetLayerAnimationInfo
|
||||
#include "FrameLayerBuilder.h"
|
||||
@@ -260,6 +261,7 @@ RestyleManager::ContentStateChanged(nsIContent* aContent,
|
||||
ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
|
||||
|
||||
PostRestyleEvent(aElement, restyleHint, changeHint);
|
||||
RestyleForHasPseudoClassChange(aElement);
|
||||
}
|
||||
|
||||
// Forwarded nsIMutationObserver method, to handle restyling.
|
||||
@@ -371,6 +373,30 @@ RestyleManager::AttributeChanged(Element* aElement,
|
||||
aOldValue,
|
||||
rsdata);
|
||||
PostRestyleEvent(aElement, rshint, hint, &rsdata);
|
||||
RestyleForHasPseudoClassChange(aElement);
|
||||
}
|
||||
|
||||
bool
|
||||
RestyleManager::RestyleForHasPseudoClassChange(nsINode* aNode)
|
||||
{
|
||||
Element* affectedRoot = nullptr;
|
||||
for (nsINode* node = aNode; node; node = node->GetParentNode()) {
|
||||
if (!node->GetProperty(nsGkAtoms::hasSelectorDependency)) {
|
||||
continue;
|
||||
}
|
||||
if (node->IsElement()) {
|
||||
affectedRoot = node->AsElement();
|
||||
} else if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
|
||||
affectedRoot = shadow->GetHost();
|
||||
}
|
||||
}
|
||||
|
||||
if (!affectedRoot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PostRestyleEvent(affectedRoot, eRestyle_Subtree, nsChangeHint(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ uint64_t
|
||||
@@ -399,6 +425,10 @@ RestyleManager::RestyleForAppend(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent)
|
||||
{
|
||||
// The container cannot be a document, but might be a ShadowRoot.
|
||||
if (RestyleForHasPseudoClassChange(aContainer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aContainer->IsElement()) {
|
||||
return;
|
||||
}
|
||||
@@ -488,6 +518,10 @@ RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
|
||||
nsIContent* aChild)
|
||||
{
|
||||
// The container might be a document or a ShadowRoot.
|
||||
if (RestyleForHasPseudoClassChange(aContainer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aContainer->IsElement()) {
|
||||
return;
|
||||
}
|
||||
@@ -578,6 +612,10 @@ RestyleManager::ContentRemoved(nsINode* aContainer,
|
||||
nsIContent* aFollowingSibling)
|
||||
{
|
||||
// The container might be a document or a ShadowRoot.
|
||||
if (RestyleForHasPseudoClassChange(aContainer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aContainer->IsElement()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -314,6 +314,10 @@ 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.
|
||||
bool RestyleForHasPseudoClassChange(nsINode* aNode);
|
||||
|
||||
// Process any pending restyles. This should be called after
|
||||
// CreateNeededFrames.
|
||||
// Note: It's the caller's responsibility to make sure to wrap a
|
||||
|
||||
@@ -993,7 +993,8 @@ nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
nsCSSSelectorList::nsCSSSelectorList(void)
|
||||
: mSelectors(nullptr),
|
||||
mWeight(0),
|
||||
mNext(nullptr)
|
||||
mNext(nullptr),
|
||||
mIsRelativeSelector(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsCSSSelectorList);
|
||||
}
|
||||
@@ -1047,7 +1048,33 @@ nsCSSSelectorList::ToString(nsAString& aResult, CSSStyleSheet* aSheet)
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
p->mSelectors->ToString(aResult, aSheet, true);
|
||||
if (p->mIsRelativeSelector) {
|
||||
// :has() arguments are represented internally as selectors with an
|
||||
// implicit leftmost anchor. Serialize the authored relative selector,
|
||||
// including an explicit leading combinator when there is one.
|
||||
const nsCSSSelector* leftmost = p->mSelectors;
|
||||
while (leftmost->mNext && leftmost->mNext->mNext) {
|
||||
leftmost = leftmost->mNext;
|
||||
}
|
||||
MOZ_ASSERT(leftmost->mNext,
|
||||
"relative selector should have an implicit anchor");
|
||||
if (leftmost->mNext->mOperator != char16_t(' ')) {
|
||||
aResult.Append(leftmost->mNext->mOperator);
|
||||
aResult.Append(char16_t(' '));
|
||||
}
|
||||
|
||||
nsAutoPtr<nsCSSSelector> selectors(p->mSelectors->Clone());
|
||||
nsCSSSelector* beforeAnchor = selectors;
|
||||
while (beforeAnchor->mNext && beforeAnchor->mNext->mNext) {
|
||||
beforeAnchor = beforeAnchor->mNext;
|
||||
}
|
||||
MOZ_ASSERT(beforeAnchor->mNext);
|
||||
delete beforeAnchor->mNext;
|
||||
beforeAnchor->mNext = nullptr;
|
||||
selectors->ToString(aResult, aSheet, true);
|
||||
} else {
|
||||
p->mSelectors->ToString(aResult, aSheet, true);
|
||||
}
|
||||
p = p->mNext;
|
||||
if (!p)
|
||||
break;
|
||||
@@ -1060,6 +1087,7 @@ nsCSSSelectorList::Clone(bool aDeep) const
|
||||
{
|
||||
nsCSSSelectorList *result = new nsCSSSelectorList();
|
||||
result->mWeight = mWeight;
|
||||
result->mIsRelativeSelector = mIsRelativeSelector;
|
||||
NS_IF_CLONE(mSelectors);
|
||||
|
||||
if (aDeep) {
|
||||
|
||||
@@ -314,6 +314,10 @@ struct nsCSSSelectorList {
|
||||
nsCSSSelector* mSelectors;
|
||||
int32_t mWeight;
|
||||
nsCSSSelectorList* mNext;
|
||||
// Relative selector lists used by :has() have an implicit internal anchor
|
||||
// stored as their leftmost selector. This flag keeps that implementation
|
||||
// detail out of serialization and is preserved by cloning.
|
||||
bool mIsRelativeSelector;
|
||||
protected:
|
||||
friend class inDOMUtils;
|
||||
nsCSSSelectorList* Clone(bool aDeep) const;
|
||||
|
||||
@@ -127,7 +127,9 @@ enum class SelectorParsingFlags {
|
||||
eDisallowCombinators = 1 << 2,
|
||||
eDisallowPseudoElements = 1 << 3,
|
||||
eInheritNamespace = 1 << 4,
|
||||
eForceEmptyList = 1 << 5
|
||||
eForceEmptyList = 1 << 5,
|
||||
eIsRelativeSelector = 1 << 6,
|
||||
eInsideHas = 1 << 7
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorParsingFlags)
|
||||
|
||||
@@ -6452,6 +6454,28 @@ CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList,
|
||||
char16_t combinator = 0;
|
||||
nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
|
||||
|
||||
if (aFlags & SelectorParsingFlags::eIsRelativeSelector) {
|
||||
// A relative selector is evaluated as though it begins with an internal
|
||||
// anchor and a combinator. This must not use :scope: an explicit :scope
|
||||
// in the argument continues to refer to the caller's scoping root.
|
||||
// If the combinator is omitted, it is a descendant combinator.
|
||||
nsCSSSelector* anchor = list->AddSelector(char16_t(0));
|
||||
anchor->AddPseudoClass(CSSPseudoClassType::mozHasRelativeAnchor);
|
||||
list->mIsRelativeSelector = true;
|
||||
|
||||
combinator = char16_t(' ');
|
||||
if (GetToken(true)) {
|
||||
if (mToken.mType == eCSSToken_Symbol &&
|
||||
(mToken.mSymbol == '+' ||
|
||||
mToken.mSymbol == '>' ||
|
||||
mToken.mSymbol == '~')) {
|
||||
combinator = mToken.mSymbol;
|
||||
} else {
|
||||
UngetToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (!ParseSelector(list, combinator, aFlags)) {
|
||||
return false;
|
||||
@@ -7125,6 +7149,9 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||
if (aFlags & SelectorParsingFlags::eDisallowCombinators) {
|
||||
flags |= SelectorParsingFlags::eDisallowCombinators;
|
||||
}
|
||||
if (aFlags & SelectorParsingFlags::eInsideHas) {
|
||||
flags |= SelectorParsingFlags::eInsideHas;
|
||||
}
|
||||
if (aFlags & SelectorParsingFlags::eForceEmptyList ||
|
||||
forceEmptyList) {
|
||||
flags |= SelectorParsingFlags::eForceEmptyList;
|
||||
@@ -7570,7 +7597,15 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
|
||||
bool isSingleSelector =
|
||||
nsCSSPseudoClasses::HasSingleSelectorArg(aType);
|
||||
|
||||
if (nsCSSPseudoClasses::HasForgivingSelectorListArg(aType)) {
|
||||
if (aType == CSSPseudoClassType::has) {
|
||||
// :has() takes an unforgiving <relative-selector-list>, cannot be nested,
|
||||
// and cannot contain pseudo-elements.
|
||||
if (aFlags & SelectorParsingFlags::eInsideHas) {
|
||||
return eSelectorParsingStatus_Error;
|
||||
}
|
||||
aFlags |= SelectorParsingFlags::eIsRelativeSelector |
|
||||
SelectorParsingFlags::eInsideHas;
|
||||
} else if (nsCSSPseudoClasses::HasForgivingSelectorListArg(aType)) {
|
||||
aFlags |= SelectorParsingFlags::eIsForgiving;
|
||||
} else if (isSingleSelector || aType == CSSPseudoClassType::mozAny) {
|
||||
aFlags |= SelectorParsingFlags::eDisallowCombinators;
|
||||
|
||||
@@ -77,6 +77,9 @@ CSS_PSEUDO_CLASS(is, ":is", 0, "layout.css.is-where-pseudo.enabled")
|
||||
CSS_PSEUDO_CLASS(matches, ":matches", 0, "layout.css.is-where-pseudo.enabled")
|
||||
CSS_PSEUDO_CLASS(any, ":any", 0, "layout.css.is-where-pseudo.enabled")
|
||||
CSS_PSEUDO_CLASS(where, ":where", 0, "layout.css.is-where-pseudo.enabled")
|
||||
CSS_PSEUDO_CLASS(has, ":has", 0, "")
|
||||
CSS_PSEUDO_CLASS(mozHasRelativeAnchor, ":-moz-has-relative-anchor",
|
||||
CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "")
|
||||
|
||||
CSS_PSEUDO_CLASS(firstChild, ":first-child", 0, "")
|
||||
CSS_PSEUDO_CLASS(firstNode, ":-moz-first-node", 0, "")
|
||||
|
||||
@@ -159,6 +159,7 @@ bool
|
||||
nsCSSPseudoClasses::HasSelectorListArg(Type aType)
|
||||
{
|
||||
return HasForgivingSelectorListArg(aType) ||
|
||||
aType == Type::has ||
|
||||
aType == Type::negation ||
|
||||
aType == Type::mozAny ||
|
||||
aType == Type::mozAnyPrivate ||
|
||||
|
||||
+171
-27
@@ -887,20 +887,34 @@ nsCSSRuleUtils::SelectorMatches(Element* aElement,
|
||||
}
|
||||
break;
|
||||
|
||||
case CSSPseudoClassType::is:
|
||||
case CSSPseudoClassType::matches:
|
||||
case CSSPseudoClassType::any:
|
||||
case CSSPseudoClassType::where: {
|
||||
case CSSPseudoClassType::is:
|
||||
case CSSPseudoClassType::matches:
|
||||
case CSSPseudoClassType::any:
|
||||
case CSSPseudoClassType::where: {
|
||||
if (!SelectorListMatches(aElement,
|
||||
pseudoClass,
|
||||
aNodeMatchContext,
|
||||
aTreeMatchContext,
|
||||
true)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case CSSPseudoClassType::mozAny: {
|
||||
}
|
||||
} break;
|
||||
|
||||
case CSSPseudoClassType::has: {
|
||||
if (!RelativeSelectorListMatches(aElement,
|
||||
pseudoClass->u.mSelectorList,
|
||||
aTreeMatchContext)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case CSSPseudoClassType::mozHasRelativeAnchor:
|
||||
if (aElement != aTreeMatchContext.mRelativeSelectorAnchor) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CSSPseudoClassType::mozAny: {
|
||||
// XXX: For compatibility, we retain :-moz-any()'s original behavior,
|
||||
// which is to be unforgiving and reject complex selectors in
|
||||
// its selector list argument.
|
||||
@@ -1626,8 +1640,10 @@ nsCSSRuleUtils::SelectorListMatches(Element* aElement,
|
||||
SelectorMatchesTreeFlags selectorTreeFlags = SelectorMatchesTreeFlags(0);
|
||||
// Try to look for the closest ancestor link element if we're processing
|
||||
// the selector list argument of a pseudo-class, but only if for a new style context (see SelectorMatches).
|
||||
if (!aNodeMatchContext.mIsRelevantLink && aTreeMatchContext.mForStyling &&
|
||||
(aSelectorFlags & SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
|
||||
if (!aNodeMatchContext.mIsRelevantLink &&
|
||||
!aTreeMatchContext.mRelativeSelectorAnchor &&
|
||||
aTreeMatchContext.mForStyling &&
|
||||
(aSelectorFlags & SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
|
||||
selectorTreeFlags = eLookForRelevantLink;
|
||||
}
|
||||
|
||||
@@ -1644,23 +1660,151 @@ nsCSSRuleUtils::SelectorListMatches(Element* aElement,
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsCSSRuleUtils::SelectorListMatches(Element* aElement,
|
||||
nsPseudoClassList* aList,
|
||||
NodeMatchContext& aNodeMatchContext,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
bool aIsForgiving,
|
||||
bool aPreventComplexSelectors)
|
||||
{
|
||||
return SelectorListMatches(aElement,
|
||||
aList->u.mSelectorList,
|
||||
aNodeMatchContext,
|
||||
aTreeMatchContext,
|
||||
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT,
|
||||
aIsForgiving,
|
||||
aPreventComplexSelectors);
|
||||
}
|
||||
|
||||
// TreeMatchContext and AncestorFilter out of line methods
|
||||
nsCSSRuleUtils::SelectorListMatches(Element* aElement,
|
||||
nsPseudoClassList* aList,
|
||||
NodeMatchContext& aNodeMatchContext,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
bool aIsForgiving,
|
||||
bool aPreventComplexSelectors)
|
||||
{
|
||||
return SelectorListMatches(aElement,
|
||||
aList->u.mSelectorList,
|
||||
aNodeMatchContext,
|
||||
aTreeMatchContext,
|
||||
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT,
|
||||
aIsForgiving,
|
||||
aPreventComplexSelectors);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
matchesCandidate(Element* aCandidate,
|
||||
nsCSSSelectorList* aRelativeSelector,
|
||||
TreeMatchContext& aTreeMatchContext)
|
||||
{
|
||||
AutoRestore<Element*> styleScopeRestorer(
|
||||
aTreeMatchContext.mCurrentStyleScope);
|
||||
// A candidate inspected by :has() is never the relevant link for the
|
||||
// anchor being styled. Treating it as relevant would allow
|
||||
// :has(:visited) to expose link history through an ancestor.
|
||||
NodeMatchContext nodeContext(EventStates(), false);
|
||||
nsCSSSelector* selector = aRelativeSelector->mSelectors;
|
||||
if (!nsCSSRuleUtils::SelectorMatches(
|
||||
aCandidate,
|
||||
selector,
|
||||
nodeContext,
|
||||
aTreeMatchContext,
|
||||
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
|
||||
return false;
|
||||
}
|
||||
return !selector->mNext ||
|
||||
nsCSSRuleUtils::SelectorMatchesTree(
|
||||
aCandidate,
|
||||
selector->mNext,
|
||||
aTreeMatchContext,
|
||||
SelectorMatchesTreeFlags(0));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
matchesSubtree(nsIContent* aRoot,
|
||||
nsCSSSelectorList* aRelativeSelector,
|
||||
TreeMatchContext& aTreeMatchContext)
|
||||
{
|
||||
for (nsIContent* node = aRoot;
|
||||
node;
|
||||
node = node->GetNextNode(aRoot)) {
|
||||
if (node->IsElement() &&
|
||||
matchesCandidate(node->AsElement(),
|
||||
aRelativeSelector,
|
||||
aTreeMatchContext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsCSSRuleUtils::RelativeSelectorListMatches(
|
||||
Element* aAnchor,
|
||||
nsCSSSelectorList* aList,
|
||||
TreeMatchContext& aTreeMatchContext)
|
||||
{
|
||||
MOZ_ASSERT(aAnchor);
|
||||
MOZ_ASSERT(aList);
|
||||
|
||||
// :has() reverses the usual direction of selector invalidation. The
|
||||
// restyle manager uses these conservative markers to promote changes in the
|
||||
// anchor's descendants, or in following sibling subtrees, back to a subtree
|
||||
// that contains the anchor.
|
||||
if (aTreeMatchContext.mForStyling) {
|
||||
aAnchor->SetProperty(nsGkAtoms::hasSelectorDependency,
|
||||
reinterpret_cast<void*>(1));
|
||||
nsIContent* parent = aAnchor->GetParent();
|
||||
if (parent) {
|
||||
parent->SetProperty(nsGkAtoms::hasSelectorDependency,
|
||||
reinterpret_cast<void*>(1));
|
||||
}
|
||||
}
|
||||
|
||||
AutoRestore<Element*> relativeAnchorRestorer(
|
||||
aTreeMatchContext.mRelativeSelectorAnchor);
|
||||
aTreeMatchContext.mRelativeSelectorAnchor = aAnchor;
|
||||
|
||||
for (nsCSSSelectorList* relative = aList;
|
||||
relative;
|
||||
relative = relative->mNext) {
|
||||
MOZ_ASSERT(relative->mIsRelativeSelector);
|
||||
MOZ_ASSERT(relative->mSelectors && relative->mSelectors->mNext);
|
||||
|
||||
// Find the compound immediately to the right of the implicit internal
|
||||
// anchor. This selector representation stores a combinator on its
|
||||
// left-hand selector, so the anchor owns the leading combinator.
|
||||
nsCSSSelector* leftmost = relative->mSelectors;
|
||||
while (leftmost->mNext && leftmost->mNext->mNext) {
|
||||
leftmost = leftmost->mNext;
|
||||
}
|
||||
MOZ_ASSERT(leftmost->mNext);
|
||||
|
||||
switch (leftmost->mNext->mOperator) {
|
||||
// Even for a leading adjacent-sibling combinator, a later combinator in
|
||||
// the relative selector can move the rightmost matching element into a
|
||||
// subsequent sibling subtree (for example, :has(+ .a + .b)). Search
|
||||
// every following subtree and let the full selector enforce the exact
|
||||
// leading relationship to the anchor.
|
||||
case char16_t('+'):
|
||||
case char16_t('~'):
|
||||
for (Element* sibling = aAnchor->GetNextElementSibling();
|
||||
sibling;
|
||||
sibling = sibling->GetNextElementSibling()) {
|
||||
if (matchesSubtree(sibling, relative, aTreeMatchContext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case char16_t('>'):
|
||||
case char16_t(' '):
|
||||
for (nsIContent* node = aAnchor->GetFirstChild();
|
||||
node;
|
||||
node = node->GetNextNode(aAnchor)) {
|
||||
if (node->IsElement() &&
|
||||
matchesCandidate(node->AsElement(),
|
||||
relative,
|
||||
aTreeMatchContext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false, "unexpected relative selector combinator");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TreeMatchContext and AncestorFilter out of line methods
|
||||
void
|
||||
TreeMatchContext::InitAncestors(Element* aElement)
|
||||
{
|
||||
|
||||
@@ -69,13 +69,18 @@ struct nsCSSRuleUtils
|
||||
bool aIsForgiving = false,
|
||||
bool aPreventComplexSelectors = false);
|
||||
|
||||
static bool SelectorListMatches(Element* aElement,
|
||||
nsPseudoClassList* aList,
|
||||
NodeMatchContext& aNodeMatchContext,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
bool aIsForgiving = false,
|
||||
bool aPreventComplexSelectors = false);
|
||||
|
||||
static bool SelectorListMatches(Element* aElement,
|
||||
nsPseudoClassList* aList,
|
||||
NodeMatchContext& aNodeMatchContext,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
bool aIsForgiving = false,
|
||||
bool aPreventComplexSelectors = false);
|
||||
|
||||
static bool RelativeSelectorListMatches(
|
||||
Element* aAnchor,
|
||||
nsCSSSelectorList* aList,
|
||||
TreeMatchContext& aTreeMatchContext);
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool HasPseudoClassSelectorArgsWithCombinators(
|
||||
nsCSSSelector* aSelector);
|
||||
|
||||
@@ -449,6 +449,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||
, mCurrentStyleScope(nullptr)
|
||||
, mIsTopmostScope(false)
|
||||
, mForAssignedSlot(false)
|
||||
, mRelativeSelectorAnchor(nullptr)
|
||||
{
|
||||
if (aMatchVisited != eNeverMatchVisited) {
|
||||
nsILoadContext* loadContext = mDocument->GetLoadContext();
|
||||
@@ -466,6 +467,11 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||
nsRuleWalker::eRelevantLinkUnvisited,
|
||||
aDocument)
|
||||
{}
|
||||
|
||||
// The anchor used by the implementation-only leftmost compound of a
|
||||
// relative selector. This is deliberately separate from mScopes: an
|
||||
// explicit :scope inside :has() still refers to the API/style scope.
|
||||
mozilla::dom::Element* mRelativeSelectorAnchor;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS RuleProcessorData {
|
||||
|
||||
@@ -250,6 +250,7 @@ support-files = redundant_font_download.sjs
|
||||
[test_rule_insertion.html]
|
||||
[test_rule_serialization.html]
|
||||
[test_rules_out_of_sheets.html]
|
||||
[test_has_pseudo_class.html]
|
||||
[test_selectors.html]
|
||||
[test_selectors_on_anonymous_content.html]
|
||||
[test_setPropertyWithNull.html]
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS :has() relational pseudo-class</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
<style id="has-styles">
|
||||
.dynamic,
|
||||
.structural,
|
||||
.sibling-anchor,
|
||||
.state-anchor,
|
||||
.empty-anchor {
|
||||
color: rgb(0, 0, 255);
|
||||
}
|
||||
.dynamic:has(.watched),
|
||||
.structural:has(> .inserted),
|
||||
.sibling-anchor:has(+ .sibling-target.active),
|
||||
.state-anchor:has(input:checked),
|
||||
.empty-anchor:has(> :empty) {
|
||||
color: rgb(255, 0, 0);
|
||||
}
|
||||
|
||||
.specificity.other {
|
||||
color: rgb(0, 0, 255);
|
||||
}
|
||||
/* The maximum specificity in the list is used even when that list item
|
||||
does not match. */
|
||||
.specificity:has(.specificity-child, #does-not-exist) {
|
||||
color: rgb(255, 0, 0);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="descendant-anchor">
|
||||
<div class="descendant"><span class="deep"></span></div>
|
||||
<p class="direct"></p>
|
||||
</div>
|
||||
<div id="nonmatching-anchor"><div class="descendant"></div></div>
|
||||
|
||||
<h2 id="sibling-query-anchor"></h2>
|
||||
<p id="adjacent"><span class="inside-adjacent"></span></p>
|
||||
<p class="later-sibling"></p>
|
||||
|
||||
<div id="dynamic" class="dynamic"><span id="dynamic-child"></span></div>
|
||||
<div id="structural" class="structural"></div>
|
||||
<div id="sibling-anchor" class="sibling-anchor"></div>
|
||||
<div id="sibling-target" class="sibling-target"></div>
|
||||
<div id="state-anchor" class="state-anchor"><input id="checkbox" type="checkbox"></div>
|
||||
<div id="empty-anchor" class="empty-anchor"><span id="empty-child"></span></div>
|
||||
<div id="specificity" class="specificity other"><span class="specificity-child"></span></div>
|
||||
<div id="explicit-outer" class="explicit-outer">
|
||||
<div id="explicit-scope"><span class="scope-result"></span></div>
|
||||
</div>
|
||||
<div id="visited-anchor"><a href=""></a></div>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const blue = "rgb(0, 0, 255)";
|
||||
const red = "rgb(255, 0, 0)";
|
||||
|
||||
function color(id) {
|
||||
return getComputedStyle(document.getElementById(id)).color;
|
||||
}
|
||||
|
||||
function syntaxError(selector) {
|
||||
let threw = false;
|
||||
try {
|
||||
document.querySelector(selector);
|
||||
} catch (error) {
|
||||
threw = error.name === "SyntaxError";
|
||||
}
|
||||
ok(threw, selector + " is rejected as an invalid selector");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
is(document.querySelector("div:has(.deep)").id,
|
||||
"descendant-anchor",
|
||||
"implicit descendant relative selector matches");
|
||||
is(document.querySelector("div:has(> .direct)").id,
|
||||
"descendant-anchor",
|
||||
"leading child combinator matches");
|
||||
is(document.querySelector("h2:has(+ p .inside-adjacent)").id,
|
||||
"sibling-query-anchor",
|
||||
"adjacent sibling subtree matches");
|
||||
is(document.querySelector("h2:has(+ p + .later-sibling)").id,
|
||||
"sibling-query-anchor",
|
||||
"later combinators can move beyond the adjacent sibling subtree");
|
||||
is(document.querySelector("h2:has(~ .later-sibling)").id,
|
||||
"sibling-query-anchor",
|
||||
"subsequent sibling matches");
|
||||
ok(!document.getElementById("nonmatching-anchor").matches(":has(.deep)"),
|
||||
"a descendant of the wrong compound does not match");
|
||||
is(document.querySelector(".inside-adjacent").closest("p:has(.inside-adjacent)").id,
|
||||
"adjacent",
|
||||
":has() works through closest()");
|
||||
|
||||
syntaxError("div:has()");
|
||||
syntaxError("div:has(.deep, :definitely-unknown)");
|
||||
syntaxError("div:has(:has(.deep))");
|
||||
syntaxError("div:has(::before)");
|
||||
|
||||
let explicitScope = document.getElementById("explicit-scope");
|
||||
is(explicitScope.querySelectorAll(":has(:scope)").length, 0,
|
||||
"explicit :scope is distinct from the implicit relative anchor");
|
||||
is(explicitScope.querySelectorAll(
|
||||
".explicit-outer:has(:scope) .scope-result").length, 1,
|
||||
"explicit :scope inside :has() refers to the query scoping root");
|
||||
|
||||
ok(document.documentElement.matches(":has(:is(:has(*), body))"),
|
||||
"an invalid nested :has() branch is discarded by a forgiving :is()");
|
||||
ok(!document.documentElement.matches(":has(:is(:has(*)))"),
|
||||
"a forgiving list containing only invalid nested :has() matches nothing");
|
||||
|
||||
ok(!document.getElementById("visited-anchor").matches(":has(:visited)"),
|
||||
":visited does not expose history through :has()");
|
||||
ok(document.getElementById("visited-anchor").matches(":has(:link)"),
|
||||
":link remains usable inside :has()");
|
||||
|
||||
let sheet = document.getElementById("has-styles").sheet;
|
||||
let index = sheet.insertRule("article:has(> img, + aside .note) {}", sheet.cssRules.length);
|
||||
is(sheet.cssRules[index].selectorText,
|
||||
"article:has(> img, + aside .note)",
|
||||
"relative selector list serialization does not expose the implicit anchor");
|
||||
sheet.deleteRule(index);
|
||||
|
||||
is(color("specificity"), red,
|
||||
":has() uses the most specific selector in its complete argument list");
|
||||
|
||||
is(color("dynamic"), blue, "descendant attribute dependency starts unmatched");
|
||||
document.getElementById("dynamic-child").classList.add("watched");
|
||||
is(color("dynamic"), red, "descendant attribute change makes :has() match");
|
||||
document.getElementById("dynamic-child").classList.remove("watched");
|
||||
is(color("dynamic"), blue, "descendant attribute change makes :has() stop matching");
|
||||
|
||||
is(color("structural"), blue, "child dependency starts unmatched");
|
||||
let inserted = document.createElement("span");
|
||||
inserted.className = "inserted";
|
||||
document.getElementById("structural").appendChild(inserted);
|
||||
is(color("structural"), red, "child insertion makes :has() match");
|
||||
inserted.remove();
|
||||
is(color("structural"), blue, "child removal makes :has() stop matching");
|
||||
|
||||
is(color("sibling-anchor"), blue, "sibling dependency starts unmatched");
|
||||
document.getElementById("sibling-target").classList.add("active");
|
||||
is(color("sibling-anchor"), red, "following sibling attribute change invalidates anchor");
|
||||
document.getElementById("sibling-target").classList.remove("active");
|
||||
is(color("sibling-anchor"), blue, "following sibling can stop matching dynamically");
|
||||
|
||||
is(color("state-anchor"), blue, "state dependency starts unmatched");
|
||||
document.getElementById("checkbox").checked = true;
|
||||
is(color("state-anchor"), red, "descendant state change invalidates anchor");
|
||||
document.getElementById("checkbox").checked = false;
|
||||
is(color("state-anchor"), blue, "descendant state can stop matching dynamically");
|
||||
|
||||
is(color("empty-anchor"), red, ":empty inside :has() initially matches");
|
||||
document.getElementById("empty-child").appendChild(document.createTextNode("text"));
|
||||
is(color("empty-anchor"), blue, "text insertion invalidates :empty inside :has()");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -79,7 +79,7 @@ class FasterMakeBackend(CommonBackend, PartialBackend):
|
||||
.add_pattern_symlink(
|
||||
prefix,
|
||||
f.full_path[len(prefix):],
|
||||
mozpath.join(obj.install_target, path))
|
||||
path)
|
||||
else:
|
||||
self._install_manifests[obj.install_target].add_symlink(
|
||||
f.full_path,
|
||||
|
||||
@@ -481,7 +481,7 @@ class PreprocessedFile(BaseFile):
|
||||
pp = Preprocessor(defines=self.defines, marker=self.marker)
|
||||
pp.setSilenceDirectiveWarnings(self.silence_missing_directive_warnings)
|
||||
|
||||
with open(self.path, 'rU') as input:
|
||||
with open(self.path, 'r') as input:
|
||||
pp.processFile(input=input, output=dest, depfile=deps_out)
|
||||
|
||||
dest.close()
|
||||
|
||||
@@ -78,12 +78,6 @@ class ToolLauncher(object):
|
||||
env['PATH'] = '%s;%s' % (vcdir, env['PATH'])
|
||||
break
|
||||
|
||||
# Work around a bug in Python 2.7.2 and lower where unicode types in
|
||||
# environment variables aren't handled by subprocess.
|
||||
for k, v in list(env.items()):
|
||||
if isinstance(v, str):
|
||||
env[k] = v.encode('utf-8')
|
||||
|
||||
print('Executing', ' '.join(cmd), file=errors.out)
|
||||
errors.out.flush()
|
||||
return subprocess.call(cmd, env=env)
|
||||
|
||||
@@ -5186,13 +5186,6 @@
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
ReadINIStr $R8 $R7 "Install" "QuickLaunchShortcut"
|
||||
${If} $R8 == "false"
|
||||
StrCpy $AddQuickLaunchSC "0"
|
||||
${Else}
|
||||
StrCpy $AddQuickLaunchSC "1"
|
||||
${EndIf}
|
||||
|
||||
ReadINIStr $R8 $R7 "Install" "DesktopShortcut"
|
||||
${If} $R8 == "false"
|
||||
StrCpy $AddDesktopSC "0"
|
||||
@@ -5812,43 +5805,11 @@
|
||||
Push $0
|
||||
|
||||
!ifndef NONADMIN_ELEVATE
|
||||
${If} ${AtLeastWinVista}
|
||||
UAC::IsAdmin
|
||||
; If the user is not an admin already
|
||||
${If} "$0" != "1"
|
||||
UAC::SupportsUAC
|
||||
; If the system supports UAC
|
||||
${If} "$0" == "1"
|
||||
UAC::GetElevationType
|
||||
; If the user account has a split token
|
||||
${If} "$0" == "3"
|
||||
UAC::RunElevated
|
||||
UAC::Unload
|
||||
; Nothing besides UAC initialized so no need to call OnEndCommon
|
||||
Quit
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Else}
|
||||
${GetParameters} $R9
|
||||
${If} $R9 != ""
|
||||
ClearErrors
|
||||
${GetOptions} "$R9" "/UAC:" $0
|
||||
; If the command line contains /UAC then we need to initialize
|
||||
; the UAC plugin to use UAC::ExecCodeSegment to execute code in
|
||||
; the non-elevated context.
|
||||
${Unless} ${Errors}
|
||||
UAC::RunElevated
|
||||
${EndUnless}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
!else
|
||||
${If} ${AtLeastWinVista}
|
||||
UAC::IsAdmin
|
||||
; If the user is not an admin already
|
||||
${If} "$0" != "1"
|
||||
UAC::SupportsUAC
|
||||
; If the system supports UAC require that the user elevate
|
||||
; If the system supports UAC
|
||||
${If} "$0" == "1"
|
||||
UAC::GetElevationType
|
||||
; If the user account has a split token
|
||||
@@ -5858,41 +5819,69 @@
|
||||
; Nothing besides UAC initialized so no need to call OnEndCommon
|
||||
Quit
|
||||
${EndIf}
|
||||
${Else}
|
||||
; Check if UAC is enabled. If the user has turned UAC on or off
|
||||
; without rebooting this value will be incorrect. This is an
|
||||
; edgecase that we have to live with when trying to allow
|
||||
; installing when the user doesn't have privileges such as a public
|
||||
; computer while trying to also achieve UAC elevation. When this
|
||||
; happens the user will be presented with the runas dialog if the
|
||||
; value is 1 and won't be presented with the UAC dialog when the
|
||||
; value is 0.
|
||||
ReadRegDWord $R9 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableLUA"
|
||||
${If} "$R9" == "1"
|
||||
; This will display the UAC version of the runas dialog which
|
||||
; requires a password for an existing user account.
|
||||
UAC::RunElevated
|
||||
${If} "$0" == "0" ; Was elevation successful
|
||||
UAC::Unload
|
||||
; Nothing besides UAC initialized so no need to call OnEndCommon
|
||||
Quit
|
||||
${EndIf}
|
||||
; Unload UAC since the elevation request was not successful and
|
||||
; install anyway.
|
||||
UAC::Unload
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Else}
|
||||
ClearErrors
|
||||
${${_MOZFUNC_UN}GetParameters} $R9
|
||||
${${_MOZFUNC_UN}GetOptions} "$R9" "/UAC:" $R9
|
||||
; If the command line contains /UAC then we need to initialize the UAC
|
||||
; plugin to use UAC::ExecCodeSegment to execute code in the
|
||||
; non-elevated context.
|
||||
${Unless} ${Errors}
|
||||
UAC::RunElevated
|
||||
${EndUnless}
|
||||
${GetParameters} $R9
|
||||
${If} $R9 != ""
|
||||
ClearErrors
|
||||
${GetOptions} "$R9" "/UAC:" $0
|
||||
; If the command line contains /UAC then we need to initialize
|
||||
; the UAC plugin to use UAC::ExecCodeSegment to execute code in
|
||||
; the non-elevated context.
|
||||
${Unless} ${Errors}
|
||||
UAC::RunElevated
|
||||
${EndUnless}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
!else
|
||||
UAC::IsAdmin
|
||||
; If the user is not an admin already
|
||||
${If} "$0" != "1"
|
||||
UAC::SupportsUAC
|
||||
; If the system supports UAC require that the user elevate
|
||||
${If} "$0" == "1"
|
||||
UAC::GetElevationType
|
||||
; If the user account has a split token
|
||||
${If} "$0" == "3"
|
||||
UAC::RunElevated
|
||||
UAC::Unload
|
||||
; Nothing besides UAC initialized so no need to call OnEndCommon
|
||||
Quit
|
||||
${EndIf}
|
||||
${Else}
|
||||
; Check if UAC is enabled. If the user has turned UAC on or off
|
||||
; without rebooting this value will be incorrect. This is an
|
||||
; edgecase that we have to live with when trying to allow
|
||||
; installing when the user doesn't have privileges such as a public
|
||||
; computer while trying to also achieve UAC elevation. When this
|
||||
; happens the user will be presented with the runas dialog if the
|
||||
; value is 1 and won't be presented with the UAC dialog when the
|
||||
; value is 0.
|
||||
ReadRegDWord $R9 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableLUA"
|
||||
${If} "$R9" == "1"
|
||||
; This will display the UAC version of the runas dialog which
|
||||
; requires a password for an existing user account.
|
||||
UAC::RunElevated
|
||||
${If} "$0" == "0" ; Was elevation successful
|
||||
UAC::Unload
|
||||
; Nothing besides UAC initialized so no need to call OnEndCommon
|
||||
Quit
|
||||
${EndIf}
|
||||
; Unload UAC since the elevation request was not successful and
|
||||
; install anyway.
|
||||
UAC::Unload
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Else}
|
||||
ClearErrors
|
||||
${${_MOZFUNC_UN}GetParameters} $R9
|
||||
${${_MOZFUNC_UN}GetOptions} "$R9" "/UAC:" $R9
|
||||
; If the command line contains /UAC then we need to initialize the UAC
|
||||
; plugin to use UAC::ExecCodeSegment to execute code in the
|
||||
; non-elevated context.
|
||||
${Unless} ${Errors}
|
||||
UAC::RunElevated
|
||||
${EndUnless}
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
@@ -5956,10 +5945,6 @@
|
||||
!define ${_MOZFUNC_UN}UnloadUAC "!insertmacro ${_MOZFUNC_UN}UnloadUACCall"
|
||||
|
||||
Function ${_MOZFUNC_UN}UnloadUAC
|
||||
${Unless} ${AtLeastWinVista}
|
||||
Return
|
||||
${EndUnless}
|
||||
|
||||
Push $R9
|
||||
|
||||
ClearErrors
|
||||
@@ -6307,7 +6292,6 @@
|
||||
*/
|
||||
!macro _LoggingShortcutsCommon
|
||||
!insertmacro LogDesktopShortcut
|
||||
!insertmacro LogQuickLaunchShortcut
|
||||
!insertmacro LogSMProgramsShortcut
|
||||
!macroend
|
||||
!define _LoggingShortcutsCommon "!insertmacro _LoggingShortcutsCommon"
|
||||
@@ -6329,9 +6313,9 @@
|
||||
|
||||
/**
|
||||
* Adds shortcut entries to the shortcuts log ini file. This macro is primarily
|
||||
* a helper used by the LogDesktopShortcut, LogQuickLaunchShortcut, and
|
||||
* LogSMProgramsShortcut macros but it can be used by other code if desired. If
|
||||
* the value already exists the the value is not written to the file.
|
||||
* a helper used by the LogDesktopShortcut and LogSMProgramsShortcut macros but
|
||||
* it can be used by other code if desired. If the value already exists the the
|
||||
* value is not written to the file.
|
||||
*
|
||||
* @param _SECTION_NAME
|
||||
* The section name to write to in the shortcut log ini file
|
||||
@@ -6424,38 +6408,6 @@
|
||||
!verbose pop
|
||||
!macroend
|
||||
|
||||
/**
|
||||
* Adds a QuickLaunch shortcut entry to the shortcuts log ini file.
|
||||
*
|
||||
* @param _FILE_NAME
|
||||
* The shortcut file name (e.g. shortcut.lnk)
|
||||
*/
|
||||
!macro LogQuickLaunchShortcut
|
||||
|
||||
!ifndef LogQuickLaunchShortcut
|
||||
!insertmacro LogShortcut
|
||||
|
||||
!verbose push
|
||||
!verbose ${_MOZFUNC_VERBOSE}
|
||||
!define LogQuickLaunchShortcut "!insertmacro LogQuickLaunchShortcutCall"
|
||||
|
||||
Function LogQuickLaunchShortcut
|
||||
Call LogShortcut
|
||||
FunctionEnd
|
||||
|
||||
!verbose pop
|
||||
!endif
|
||||
!macroend
|
||||
|
||||
!macro LogQuickLaunchShortcutCall _FILE_NAME
|
||||
!verbose push
|
||||
!verbose ${_MOZFUNC_VERBOSE}
|
||||
Push "QUICKLAUNCH"
|
||||
Push "${_FILE_NAME}"
|
||||
Call LogQuickLaunchShortcut
|
||||
!verbose pop
|
||||
!macroend
|
||||
|
||||
/**
|
||||
* Adds a Start Menu shortcut entry to the shortcuts log ini file.
|
||||
*
|
||||
@@ -6611,10 +6563,6 @@
|
||||
!define ${_MOZFUNC_UN}SetAppLSPCategories "!insertmacro ${_MOZFUNC_UN}SetAppLSPCategoriesCall"
|
||||
|
||||
Function ${_MOZFUNC_UN}SetAppLSPCategories
|
||||
${Unless} ${AtLeastWinVista}
|
||||
Return
|
||||
${EndUnless}
|
||||
|
||||
Exch $R9
|
||||
Push $R8
|
||||
Push $R7
|
||||
@@ -6724,8 +6672,7 @@
|
||||
|
||||
StrCpy $R5 "false"
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
${AndIf} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar"
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar"
|
||||
FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\TaskBar\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar\$R7"
|
||||
@@ -6804,8 +6751,7 @@
|
||||
|
||||
StrCpy $R5 "false"
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
${AndIf} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu"
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu"
|
||||
FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\StartMenu\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu\$R7"
|
||||
@@ -6876,8 +6822,7 @@
|
||||
|
||||
StrCpy $R9 0
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
${AndIf} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar"
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar"
|
||||
FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\TaskBar\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar\$R7"
|
||||
@@ -6937,8 +6882,7 @@
|
||||
|
||||
StrCpy $R9 0
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
${AndIf} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu"
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu"
|
||||
FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\StartMenu\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu\$R7"
|
||||
@@ -7018,132 +6962,109 @@
|
||||
|
||||
StrCpy $R3 "false"
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
; installed shortcuts
|
||||
${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R6
|
||||
${If} ${FileExists} "$R6"
|
||||
; Update the Start Menu shortcuts' App ID for this application
|
||||
StrCpy $R2 -1
|
||||
${Do}
|
||||
IntOp $R2 $R2 + 1 ; Increment the counter
|
||||
ClearErrors
|
||||
ReadINIStr $R5 "$R6" "STARTMENU" "Shortcut$R2"
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
|
||||
${If} ${FileExists} "$SMPROGRAMS\$R5"
|
||||
ShellLink::GetShortCutTarget "$SMPROGRAMS\$$R5"
|
||||
Pop $R4
|
||||
${GetLongPath} "$R4" $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$SMPROGRAMS\$R5" "$R8"
|
||||
Pop $R4
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
|
||||
; Update the Quick Launch shortcuts' App ID for this application
|
||||
StrCpy $R2 -1
|
||||
${Do}
|
||||
IntOp $R2 $R2 + 1 ; Increment the counter
|
||||
ClearErrors
|
||||
ReadINIStr $R5 "$R6" "QUICKLAUNCH" "Shortcut$R2"
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
|
||||
${If} ${FileExists} "$QUICKLAUNCH\$R5"
|
||||
ShellLink::GetShortCutTarget "$QUICKLAUNCH\$R5"
|
||||
Pop $R4
|
||||
${GetLongPath} "$R4" $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$QUICKLAUNCH\$R5" "$R8"
|
||||
Pop $R4
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
|
||||
; Update the Start Menu Programs shortcuts' App ID for this application
|
||||
; installed shortcuts
|
||||
${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R6
|
||||
${If} ${FileExists} "$R6"
|
||||
; Update the Start Menu shortcuts' App ID for this application
|
||||
StrCpy $R2 -1
|
||||
${Do}
|
||||
IntOp $R2 $R2 + 1 ; Increment the counter
|
||||
ClearErrors
|
||||
ReadINIStr $R7 "$R6" "SMPROGRAMS" "RelativePathToDir"
|
||||
${Unless} ${Errors}
|
||||
${${_MOZFUNC_UN}GetLongPath} "$SMPROGRAMS\$R7" $R7
|
||||
${Unless} "$R7" == ""
|
||||
StrCpy $R2 -1
|
||||
${Do}
|
||||
IntOp $R2 $R2 + 1 ; Increment the counter
|
||||
ClearErrors
|
||||
ReadINIStr $R5 "$R6" "SMPROGRAMS" "Shortcut$R2"
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
ReadINIStr $R5 "$R6" "STARTMENU" "Shortcut$R2"
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
|
||||
${If} ${FileExists} "$R7\$R5"
|
||||
ShellLink::GetShortCutTarget "$R7\$R5"
|
||||
${If} ${FileExists} "$SMPROGRAMS\$R5"
|
||||
ShellLink::GetShortCutTarget "$SMPROGRAMS\$$R5"
|
||||
Pop $R4
|
||||
${GetLongPath} "$R4" $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$SMPROGRAMS\$R5" "$R8"
|
||||
Pop $R4
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
|
||||
; Update the Start Menu Programs shortcuts' App ID for this application
|
||||
ClearErrors
|
||||
ReadINIStr $R7 "$R6" "SMPROGRAMS" "RelativePathToDir"
|
||||
${Unless} ${Errors}
|
||||
${${_MOZFUNC_UN}GetLongPath} "$SMPROGRAMS\$R7" $R7
|
||||
${Unless} "$R7" == ""
|
||||
StrCpy $R2 -1
|
||||
${Do}
|
||||
IntOp $R2 $R2 + 1 ; Increment the counter
|
||||
ClearErrors
|
||||
ReadINIStr $R5 "$R6" "SMPROGRAMS" "Shortcut$R2"
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
|
||||
${If} ${FileExists} "$R7\$R5"
|
||||
ShellLink::GetShortCutTarget "$R7\$R5"
|
||||
Pop $R4
|
||||
${GetLongPath} "$R4" $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$R7\$R5" "$R8"
|
||||
Pop $R4
|
||||
${GetLongPath} "$R4" $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$R7\$R5" "$R8"
|
||||
Pop $R4
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
${EndUnless}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
${EndUnless}
|
||||
${EndIf}
|
||||
|
||||
StrCpy $R7 "$QUICKLAUNCH\User Pinned"
|
||||
StrCpy $R3 "false"
|
||||
|
||||
; $R9 = main application executable path
|
||||
; $R8 = appid
|
||||
; $R7 = user pinned path
|
||||
; $R6 = find handle
|
||||
; $R5 = found filename
|
||||
; $R4 = GetShortCutTarget result
|
||||
|
||||
; TaskBar links
|
||||
FindFirst $R6 $R5 "$R7\TaskBar\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$R7\TaskBar\$R5"
|
||||
ShellLink::GetShortCutTarget "$R7\TaskBar\$R5"
|
||||
Pop $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$R7\TaskBar\$R5" "$R8"
|
||||
Pop $R4 ; pop Set result off the stack
|
||||
StrCpy $R3 "true"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $R6 $R5
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
FindClose $R6
|
||||
|
||||
; Start menu links
|
||||
FindFirst $R6 $R5 "$R7\StartMenu\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$R7\StartMenu\$R5"
|
||||
ShellLink::GetShortCutTarget "$R7\StartMenu\$R5"
|
||||
Pop $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$R7\StartMenu\$R5" "$R8"
|
||||
Pop $R4 ; pop Set result off the stack
|
||||
StrCpy $R3 "true"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $R6 $R5
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
FindClose $R6
|
||||
${EndUnless}
|
||||
${EndIf}
|
||||
|
||||
StrCpy $R7 "$QUICKLAUNCH\User Pinned"
|
||||
StrCpy $R3 "false"
|
||||
|
||||
; $R9 = main application executable path
|
||||
; $R8 = appid
|
||||
; $R7 = user pinned path
|
||||
; $R6 = find handle
|
||||
; $R5 = found filename
|
||||
; $R4 = GetShortCutTarget result
|
||||
|
||||
; TaskBar links
|
||||
FindFirst $R6 $R5 "$R7\TaskBar\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$R7\TaskBar\$R5"
|
||||
ShellLink::GetShortCutTarget "$R7\TaskBar\$R5"
|
||||
Pop $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$R7\TaskBar\$R5" "$R8"
|
||||
Pop $R4 ; pop Set result off the stack
|
||||
StrCpy $R3 "true"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $R6 $R5
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
FindClose $R6
|
||||
|
||||
; Start menu links
|
||||
FindFirst $R6 $R5 "$R7\StartMenu\*.lnk"
|
||||
${Do}
|
||||
${If} ${FileExists} "$R7\StartMenu\$R5"
|
||||
ShellLink::GetShortCutTarget "$R7\StartMenu\$R5"
|
||||
Pop $R4
|
||||
${If} "$R4" == "$R9" ; link path == install path
|
||||
ApplicationID::Set "$R7\StartMenu\$R5" "$R8"
|
||||
Pop $R4 ; pop Set result off the stack
|
||||
StrCpy $R3 "true"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $R6 $R5
|
||||
${If} ${Errors}
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
${Loop}
|
||||
FindClose $R6
|
||||
|
||||
ClearErrors
|
||||
|
||||
StrCpy $R9 $R3
|
||||
@@ -7249,23 +7170,21 @@
|
||||
Exch $R8 ; stack: $R8, $R9 | $R8 = regpath
|
||||
Push $R7
|
||||
|
||||
${If} ${AtLeastWin7}
|
||||
${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
|
||||
; Always create a new AppUserModelID and overwrite the existing one
|
||||
; for the current installation path.
|
||||
CityHash::GetCityHash64 "$R9"
|
||||
Pop $AppUserModelID
|
||||
${If} $AppUserModelID == "error"
|
||||
GoTo end
|
||||
${EndIf}
|
||||
${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
|
||||
; Always create a new AppUserModelID and overwrite the existing one
|
||||
; for the current installation path.
|
||||
CityHash::GetCityHash64 "$R9"
|
||||
Pop $AppUserModelID
|
||||
${If} $AppUserModelID == "error"
|
||||
GoTo end
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
WriteRegStr HKLM "$R8" "$R9" "$AppUserModelID"
|
||||
${If} ${Errors}
|
||||
ClearErrors
|
||||
WriteRegStr HKLM "$R8" "$R9" "$AppUserModelID"
|
||||
WriteRegStr HKCU "$R8" "$R9" "$AppUserModelID"
|
||||
${If} ${Errors}
|
||||
ClearErrors
|
||||
WriteRegStr HKCU "$R8" "$R9" "$AppUserModelID"
|
||||
${If} ${Errors}
|
||||
StrCpy $AppUserModelID "error"
|
||||
${EndIf}
|
||||
StrCpy $AppUserModelID "error"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
@@ -7351,15 +7270,12 @@
|
||||
StrCpy $ITaskbarList3 0
|
||||
; Don't create when running silently.
|
||||
${Unless} ${Silent}
|
||||
; This is only supported on Win 7 and above.
|
||||
${If} ${AtLeastWin7}
|
||||
System::Call "ole32::CoCreateInstance(g '${CLSID_ITaskbarList}', \
|
||||
i 0, \
|
||||
i ${CLSCTX_INPROC_SERVER}, \
|
||||
g '${IID_ITaskbarList3}', \
|
||||
*i .s)"
|
||||
Pop $ITaskbarList3
|
||||
${EndIf}
|
||||
System::Call "ole32::CoCreateInstance(g '${CLSID_ITaskbarList}', \
|
||||
i 0, \
|
||||
i ${CLSCTX_INPROC_SERVER}, \
|
||||
g '${IID_ITaskbarList3}', \
|
||||
*i .s)"
|
||||
Pop $ITaskbarList3
|
||||
${EndUnless}
|
||||
FunctionEnd
|
||||
|
||||
|
||||
Reference in new issue
Block a user