Bug 508725 - Part 1: Add content flag representing whether an element is in a style scope. r=dbaron

This commit is contained in:
Cameron McCormack 2013-01-08 19:09:22 +11:00
parent ed9b0b2817
commit 993c756dda
11 changed files with 145 additions and 11 deletions

View File

@ -1319,6 +1319,9 @@ private:
NodeHasDirAuto,
// Set if a node in the node's parent chain has dir=auto.
NodeAncestorHasDirAuto,
// Set if the element is in the scope of a scoped style sheet; this flag is
// only accurate for elements bounds to a document
ElementIsInStyleScope,
// Guard value
BooleanFlagCount
};
@ -1435,6 +1438,20 @@ public:
bool NodeOrAncestorHasDirAuto() const
{ return HasDirAuto() || AncestorHasDirAuto(); }
void SetIsElementInStyleScope(bool aValue) {
MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node");
SetBoolFlag(ElementIsInStyleScope, aValue);
}
void SetIsElementInStyleScope() {
MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node");
SetBoolFlag(ElementIsInStyleScope);
}
void ClearIsElementInStyleScope() {
MOZ_ASSERT(IsElement(), "ClearIsInStyleScope on a non-Element node");
ClearBoolFlag(ElementIsInStyleScope);
}
bool IsElementInStyleScope() const { return GetBoolFlag(ElementIsInStyleScope); }
protected:
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
void SetInDocument() { SetBoolFlag(IsInDocument); }
@ -1455,7 +1472,7 @@ protected:
bool HasLockedStyleStates() const
{ return GetBoolFlag(ElementHasLockedStyleStates); }
void SetSubtreeRootPointer(nsINode* aSubtreeRoot)
void SetSubtreeRootPointer(nsINode* aSubtreeRoot)
{
NS_ASSERTION(aSubtreeRoot, "aSubtreeRoot can never be null!");
NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()), "Shouldn't be here!");

View File

@ -1155,6 +1155,9 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
// And the restyle bits
ELEMENT_ALL_RESTYLE_FLAGS);
// Propagate scoped style sheet tracking bit.
SetIsElementInStyleScope(mParent->IsElementInStyleScope());
} else {
// If we're not in the doc, update our subtree pointer.
SetSubtreeRootPointer(aParent->SubtreeRoot());

View File

@ -12,9 +12,10 @@
#include "nsStyleLinkElement.h"
#include "nsIContent.h"
#include "mozilla/css/Loader.h"
#include "mozilla/dom/Element.h"
#include "nsCSSStyleSheet.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsIDOMComment.h"
#include "nsIDOMNode.h"
@ -26,6 +27,9 @@
#include "nsUnicharInputStream.h"
#include "nsContentUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
nsStyleLinkElement::nsStyleLinkElement()
: mDontLoadStyle(false)
, mUpdatesEnabled(true)
@ -203,6 +207,87 @@ nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
aForceUpdate);
}
static bool
IsScopedStyleElement(nsIContent* aContent)
{
// This is quicker than, say, QIing aContent to nsStyleLinkElement
// and then calling its virtual GetStyleSheetInfo method to find out
// if it is scoped.
return aContent->IsHTML(nsGkAtoms::style) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped);
}
static void
SetIsElementInStyleScopeFlagOnSubtree(Element* aElement)
{
if (aElement->IsElementInStyleScope()) {
return;
}
aElement->SetIsElementInStyleScope();
nsIContent* n = aElement->GetNextNode(aElement);
while (n) {
if (n->IsElementInStyleScope()) {
n = n->GetNextNonChildNode(aElement);
} else {
if (n->IsElement()) {
n->SetIsElementInStyleScope();
}
n = n->GetNextNode(aElement);
}
}
}
static bool
HasScopedStyleSheetChild(nsIContent* aContent)
{
for (nsIContent* n = aContent->GetFirstChild(); n; n = n->GetNextSibling()) {
if (IsScopedStyleElement(n)) {
return true;
}
}
return false;
}
// Called when aElement has had a <style scoped> child removed.
static void
UpdateIsElementInStyleScopeFlagOnSubtree(Element* aElement)
{
NS_ASSERTION(aElement->IsElementInStyleScope(),
"only call UpdateIsElementInStyleScopeFlagOnSubtree on a "
"subtree that has IsElementInStyleScope boolean flag set");
if (HasScopedStyleSheetChild(aElement)) {
return;
}
aElement->ClearIsElementInStyleScope();
nsIContent* n = aElement->GetNextNode(aElement);
while (n) {
if (HasScopedStyleSheetChild(n)) {
n = n->GetNextNonChildNode(aElement);
} else {
if (n->IsElement()) {
n->ClearIsElementInStyleScope();
}
n = n->GetNextNode(aElement);
}
}
}
static Element*
GetScopeElement(nsIStyleSheet* aSheet)
{
nsRefPtr<nsCSSStyleSheet> cssStyleSheet = do_QueryObject(aSheet);
if (!cssStyleSheet) {
return nullptr;
}
return cssStyleSheet->GetScopeElement();
}
nsresult
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
nsICSSLoaderObserver* aObserver,
@ -212,6 +297,11 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
{
*aWillNotify = false;
nsCOMPtr<nsIContent> thisContent;
CallQueryInterface(this, getter_AddRefs(thisContent));
Element* oldScopeElement = GetScopeElement(mStyleSheet);
if (mStyleSheet && aOldDocument) {
// We're removing the link element from the document, unload the
// stylesheet. We want to do this even if updates are disabled, since
@ -221,15 +311,15 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
aOldDocument->RemoveStyleSheet(mStyleSheet);
aOldDocument->EndUpdate(UPDATE_STYLE);
nsStyleLinkElement::SetStyleSheet(nullptr);
if (oldScopeElement) {
UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
}
}
if (mDontLoadStyle || !mUpdatesEnabled) {
return NS_OK;
}
nsCOMPtr<nsIContent> thisContent;
QueryInterface(NS_GET_IID(nsIContent), getter_AddRefs(thisContent));
NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
@ -264,14 +354,21 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
}
nsAutoString title, type, media;
bool isScoped;
bool isAlternate;
GetStyleSheetInfo(title, type, media, &isAlternate);
GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate);
if (!type.LowerCaseEqualsLiteral("text/css")) {
return NS_OK;
}
Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr;
if (scopeElement) {
NS_ASSERTION(isInline, "non-inline style must not have scope element");
SetIsElementInStyleScopeFlagOnSubtree(scopeElement);
}
bool doneLoading = false;
nsresult rv = NS_OK;
if (isInline) {

View File

@ -73,6 +73,7 @@ protected:
virtual void GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate) = 0;
nsIStyleSheet* GetStyleSheet() { return mStyleSheet; }

View File

@ -103,6 +103,7 @@ protected:
virtual void GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate);
virtual CORSMode GetCORSMode() const;
protected:
@ -431,11 +432,13 @@ void
nsHTMLLinkElement::GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate)
{
aTitle.Truncate();
aType.Truncate();
aMedia.Truncate();
*aIsScoped = false;
*aIsAlternate = false;
nsAutoString rel;

View File

@ -86,6 +86,7 @@ protected:
void GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate);
/**
* Common method to call from the various mutation observer methods.
@ -298,6 +299,7 @@ void
nsHTMLStyleElement::GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate)
{
aTitle.Truncate();
@ -317,6 +319,8 @@ nsHTMLStyleElement::GetStyleSheetInfo(nsAString& aTitle,
GetAttr(kNameSpaceID_None, nsGkAtoms::type, aType);
*aIsScoped = HasAttr(kNameSpaceID_None, nsGkAtoms::scoped);
nsAutoString mimeType;
nsAutoString notUsed;
nsContentUtils::SplitMimeType(aType, mimeType, notUsed);

View File

@ -3,6 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Element.h"
#include "mozilla/dom/SVGStyleElement.h"
#include "nsContentUtils.h"
#include "nsStubMutationObserver.h"
@ -274,8 +275,10 @@ void
SVGStyleElement::GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate)
{
*aIsScoped = false;
*aIsAlternate = false;
nsAutoString title;

View File

@ -97,6 +97,7 @@ protected:
void GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate);
virtual CORSMode GetCORSMode() const;

View File

@ -3,6 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Element.h"
#include "nsIDOMLinkStyle.h"
#include "nsIDOMStyleSheet.h"
#include "nsIDocument.h"
@ -17,6 +18,7 @@
#include "nsContentUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
class nsXMLStylesheetPI : public nsXMLProcessingInstruction,
public nsStyleLinkElement
@ -57,6 +59,7 @@ protected:
void GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate);
virtual nsGenericDOMDataNode* CloneDataNode(nsINodeInfo *aNodeInfo,
bool aCloneText) const;
@ -177,11 +180,13 @@ void
nsXMLStylesheetPI::GetStyleSheetInfo(nsAString& aTitle,
nsAString& aType,
nsAString& aMedia,
bool* aIsScoped,
bool* aIsAlternate)
{
aTitle.Truncate();
aType.Truncate();
aMedia.Truncate();
*aIsScoped = false;
*aIsAlternate = false;
// xml-stylesheet PI is special only in prolog

View File

@ -743,7 +743,7 @@ nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode,
// Enumerate the rules in a way that cares about the order of the rules.
void
nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc,
RuleProcessorData* aData, nsIContent* aContent,
RuleProcessorData* aData, Element* aElement,
nsRuleWalker* aRuleWalker)
{
SAMPLE_LABEL("nsStyleSet", "FileRules");
@ -775,7 +775,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc,
aRuleWalker->SetLevel(eUserSheet, false, true);
bool skipUserStyles =
aContent && aContent->IsInNativeAnonymousSubtree();
aElement && aElement->IsInNativeAnonymousSubtree();
if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different
(*aCollectorFunc)(mRuleProcessors[eUserSheet], aData);
nsRuleNode* lastUserRN = aRuleWalker->CurrentNode();
@ -788,7 +788,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc,
aRuleWalker->SetLevel(eDocSheet, false, true);
bool cutOffInheritance = false;
if (mBindingManager && aContent) {
if (mBindingManager && aElement) {
// We can supply additional document-level sheets that should be walked.
mBindingManager->WalkRules(aCollectorFunc,
static_cast<ElementDependentRuleProcessorData*>(aData),

View File

@ -342,11 +342,11 @@ class nsStyleSet
// Enumerate the rules in a way that cares about the order of the
// rules.
// aContent is the node the rules are for. It might be null. aData
// aElement is the element the rules are for. It might be null. aData
// is the closure to pass to aCollectorFunc. If aContent is not null,
// aData must be a RuleProcessorData*
void FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc,
RuleProcessorData* aData, nsIContent* aContent,
RuleProcessorData* aData, mozilla::dom::Element* aElement,
nsRuleWalker* aRuleWalker);
// Enumerate all the rules in a way that doesn't care about the order