From ec64564b48a5a202e195e3fbf56ee44eefc7b035 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 11:29:52 -0400 Subject: [PATCH 01/17] Fix the reftest harness so that a 'load' test that uses reftest-wait doesn't cause following '==' tests to fail. r=roc pending --- layout/tools/reftest/reftest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index a16bc8bfd077..4bc1694aa377 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -1019,6 +1019,7 @@ function DocumentLoaded() if (gURLs[0].type == TYPE_LOAD) { ++gTestResults.LoadOnly; dump("REFTEST TEST-PASS | " + gURLs[0].prettyPath + " | (LOAD ONLY)\n"); + gCurrentCanvas = null; FinishTestItem(); return; } From a7fe40c0150d3d00d6d65d14824ff079db81839d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:04 -0400 Subject: [PATCH 02/17] Bug 479655 part 1. Add some assertions to catch it if we add too many flags. r=jst --- content/base/src/nsGenericDOMDataNode.h | 3 +++ content/html/content/src/nsGenericHTMLElement.h | 3 +++ content/xul/content/src/nsXULElement.h | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/content/base/src/nsGenericDOMDataNode.h b/content/base/src/nsGenericDOMDataNode.h index 3584fffb8418..10cde0ea3f44 100644 --- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -70,6 +70,9 @@ // This bit is set to indicate that the text may be part of a selection. #define NS_TEXT_IN_SELECTION (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 2)) +// Make sure we have enough space for those bits +PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET + 2 < 32); + class nsIDOMAttr; class nsIDOMEventListener; class nsIDOMNodeList; diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index e71919974633..19c893fecca6 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -897,6 +897,9 @@ protected: // same time, so if it becomes an issue we can probably merge them into the // same bit. --bz +// Make sure we have enough space for those bits +PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32); + //---------------------------------------------------------------------- /** diff --git a/content/xul/content/src/nsXULElement.h b/content/xul/content/src/nsXULElement.h index cd1a56222f2a..676f1479ac0a 100644 --- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -449,7 +449,10 @@ public: */ -#define XUL_ELEMENT_TEMPLATE_GENERATED 1 << NODE_TYPE_SPECIFIC_BITS_OFFSET +#define XUL_ELEMENT_TEMPLATE_GENERATED (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET) + +// Make sure we have space for our bit +PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET < 32); class nsScriptEventHandlerOwnerTearoff; From 9d715cdb019b645979a0acf07d4beda1f282ee2a Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:04 -0400 Subject: [PATCH 03/17] Bug 479655 part 1.5. Free up two more element bits. r=jst --- content/base/public/nsINode.h | 14 ++++++++++++-- content/base/src/nsGenericElement.cpp | 5 ++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 93742b4d7ca7..0890e18b029e 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -178,16 +178,26 @@ enum { // Set if the node has the accesskey attribute set. NODE_HAS_NAME = 0x00800000U, - // Four bits for the script-type ID + // Two bits for the script-type ID. Not enough to represent all + // nsIProgrammingLanguage values, but we don't care. In practice, + // we can represent the ones we want, and we can fail the others at + // runtime. NODE_SCRIPT_TYPE_OFFSET = 24, - NODE_SCRIPT_TYPE_SIZE = 4, + NODE_SCRIPT_TYPE_SIZE = 2, + + NODE_SCRIPT_TYPE_MASK = (1 << NODE_SCRIPT_TYPE_SIZE) - 1, // Remaining bits are node type specific. NODE_TYPE_SPECIFIC_BITS_OFFSET = NODE_SCRIPT_TYPE_OFFSET + NODE_SCRIPT_TYPE_SIZE }; +PR_STATIC_ASSERT(PRUint32(nsIProgrammingLanguage::JAVASCRIPT) <= + PRUint32(NODE_SCRIPT_TYPE_MASK)); +PR_STATIC_ASSERT(PRUint32(nsIProgrammingLanguage::PYTHON) <= + PRUint32(NODE_SCRIPT_TYPE_MASK)); + // Useful inline function for getting a node given an nsIContent and an // nsIDocument. Returns the first argument cast to nsINode if it is non-null, // otherwise returns the second (which may be null). We use type variables diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 71372c597b6f..a5de45755025 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -3481,14 +3481,13 @@ nsGenericElement::GetScriptTypeID() const { PtrBits flags = GetFlags(); - /* 4 bits reserved for script-type ID. */ - return (flags >> NODE_SCRIPT_TYPE_OFFSET) & 0x000F; + return (flags >> NODE_SCRIPT_TYPE_OFFSET) & NODE_SCRIPT_TYPE_MASK; } NS_IMETHODIMP nsGenericElement::SetScriptTypeID(PRUint32 aLang) { - if ((aLang & 0x000F) != aLang) { + if ((aLang & NODE_SCRIPT_TYPE_MASK) != aLang) { NS_ERROR("script ID too large!"); return NS_ERROR_FAILURE; } From e59068ace2b55b6bd0d5553bebc0ddf2955fc34a Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:04 -0400 Subject: [PATCH 04/17] Bug 479655 part 2. Add element restyle flags. r=dbaron, sr=jst --- content/base/public/Element.h | 35 +++++++++++++++++++ content/base/src/nsGenericElement.cpp | 4 ++- .../html/content/src/nsGenericHTMLElement.h | 6 ++-- content/xul/content/src/nsXULElement.h | 4 +-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/content/base/public/Element.h b/content/base/public/Element.h index ff71563f9686..16c727128852 100644 --- a/content/base/public/Element.h +++ b/content/base/public/Element.h @@ -42,6 +42,41 @@ #include "nsIContent.h" +// Element-specific flags +enum { + // Set if the element has a pending style change. + ELEMENT_HAS_PENDING_RESTYLE = (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET), + + // Set if the element is a potential restyle root (that is, has a style + // change pending _and_ that style change will attempt to restyle + // descendants). + ELEMENT_IS_POTENTIAL_RESTYLE_ROOT = + (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 1)), + + // Set if the element has a pending animation style change. + ELEMENT_HAS_PENDING_ANIMATION_RESTYLE = + (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 2)), + + // Set if the element is a potential animation restyle root (that is, + // has an animation style change pending _and_ that style change + // will attempt to restyle descendants). + ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT = + (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 3)), + + // All of those bits together, for convenience. + ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | + ELEMENT_IS_POTENTIAL_RESTYLE_ROOT | + ELEMENT_HAS_PENDING_ANIMATION_RESTYLE | + ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT, + + // Just the HAS_PENDING bits, for convenience + ELEMENT_PENDING_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | + ELEMENT_HAS_PENDING_ANIMATION_RESTYLE, + + // Remaining bits are for subclasses + ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4 +}; + namespace mozilla { namespace dom { diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index a5de45755025..0345371f1c4d 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -2882,7 +2882,9 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // Unset this flag since we now really are in a document. UnsetFlags(NODE_FORCE_XBL_BINDINGS | // And clear the lazy frame construction bits. - NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES); + NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES | + // And the restyle bits + ELEMENT_ALL_RESTYLE_FLAGS); } // If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index 19c893fecca6..ebee3ee0e18f 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -886,19 +886,19 @@ protected: // added ourselves to our mForm. It's possible to have a non-null mForm, but // not have this flag set. That happens when the form is set via the content // sink. -#define ADDED_TO_FORM (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET) +#define ADDED_TO_FORM (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) // If this flag is set on an nsGenericHTMLFormElement, that means that its form // is in the process of being unbound from the tree, and this form element // hasn't re-found its form in nsGenericHTMLFormElement::UnbindFromTree yet. -#define MAYBE_ORPHAN_FORM_ELEMENT (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET+1)) +#define MAYBE_ORPHAN_FORM_ELEMENT (1 << (ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1)) // NOTE: I don't think it's possible to have the above two flags set at the // same time, so if it becomes an issue we can probably merge them into the // same bit. --bz // Make sure we have enough space for those bits -PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32); +PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32); //---------------------------------------------------------------------- diff --git a/content/xul/content/src/nsXULElement.h b/content/xul/content/src/nsXULElement.h index 676f1479ac0a..77212a0c46c8 100644 --- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -449,10 +449,10 @@ public: */ -#define XUL_ELEMENT_TEMPLATE_GENERATED (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET) +#define XUL_ELEMENT_TEMPLATE_GENERATED (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) // Make sure we have space for our bit -PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET < 32); +PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET < 32); class nsScriptEventHandlerOwnerTearoff; From 9c63a6458d200ea54deeeff18320ab8f58bd3e3d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:04 -0400 Subject: [PATCH 05/17] Bug 479655 part 3. Refactor restyle processing into a new RestyleTracker class. r=dbaron --- layout/base/Makefile.in | 1 + layout/base/RestyleTracker.cpp | 120 +++++++++++++++++++++++ layout/base/RestyleTracker.h | 134 ++++++++++++++++++++++++++ layout/base/nsCSSFrameConstructor.cpp | 96 ++---------------- layout/base/nsCSSFrameConstructor.h | 20 +--- 5 files changed, 268 insertions(+), 103 deletions(-) create mode 100644 layout/base/RestyleTracker.cpp create mode 100644 layout/base/RestyleTracker.h diff --git a/layout/base/Makefile.in b/layout/base/Makefile.in index 2aac2f73268d..34388ff94a86 100644 --- a/layout/base/Makefile.in +++ b/layout/base/Makefile.in @@ -91,6 +91,7 @@ EXPORTS = \ CPPSRCS = \ FrameLayerBuilder.cpp \ FramePropertyTable.cpp \ + RestyleTracker.cpp \ nsCSSColorUtils.cpp \ nsCSSFrameConstructor.cpp \ nsCSSRendering.cpp \ diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp new file mode 100644 index 000000000000..151dbc44dfcc --- /dev/null +++ b/layout/base/RestyleTracker.cpp @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Boris Zbarsky (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "RestyleTracker.h" +#include "nsCSSFrameConstructor.h" + +namespace mozilla { +namespace css { + +#define RESTYLE_ARRAY_STACKSIZE 128 + +static PLDHashOperator +CollectRestyles(nsISupports* aElement, + RestyleTracker::RestyleData& aData, + void* aRestyleArrayPtr) +{ + RestyleTracker::RestyleEnumerateData** restyleArrayPtr = + static_cast + (aRestyleArrayPtr); + RestyleTracker::RestyleEnumerateData* currentRestyle = + *restyleArrayPtr; + currentRestyle->mElement = static_cast(aElement); + currentRestyle->mRestyleHint = aData.mRestyleHint; + currentRestyle->mChangeHint = aData.mChangeHint; + + // Increment to the next slot in the array + *restyleArrayPtr = currentRestyle + 1; + + return PL_DHASH_NEXT; +} + +void +RestyleTracker::ProcessRestyles() +{ + PRUint32 count = mPendingRestyles.Count(); + + // Make sure to not rebuild quote or counter lists while we're + // processing restyles + mFrameConstructor->BeginUpdate(); + + // loop so that we process any restyle events generated by processing + while (count) { + // Use the stack if we can, otherwise fall back on heap-allocation. + nsAutoTArray restyleArr; + RestyleEnumerateData* restylesToProcess = restyleArr.AppendElements(count); + + if (!restylesToProcess) { + return; + } + + RestyleEnumerateData* lastRestyle = restylesToProcess; + mPendingRestyles.Enumerate(CollectRestyles, &lastRestyle); + + NS_ASSERTION(lastRestyle - restylesToProcess == PRInt32(count), + "Enumeration screwed up somehow"); + + // Clear the hashtable so we don't end up trying to process a restyle we're + // already processing, sending us into an infinite loop. + mPendingRestyles.Clear(); + + for (RestyleEnumerateData* currentRestyle = restylesToProcess; + currentRestyle != lastRestyle; + ++currentRestyle) { + mFrameConstructor->ProcessOneRestyle(currentRestyle->mElement, + currentRestyle->mRestyleHint, + currentRestyle->mChangeHint); + } + + count = mPendingRestyles.Count(); + } + + // Set mInStyleRefresh to false now, since the EndUpdate call might + // add more restyles. + mFrameConstructor->mInStyleRefresh = PR_FALSE; + + mFrameConstructor->EndUpdate(); + +#ifdef DEBUG + mFrameConstructor->mPresShell->VerifyStyleTree(); +#endif + +} + +} // namespace css +} // namespace mozilla diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h new file mode 100644 index 000000000000..e6cc294f7f76 --- /dev/null +++ b/layout/base/RestyleTracker.h @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Boris Zbarsky (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#ifndef mozilla_css_RestyleTracker_h +#define mozilla_css_RestyleTracker_h + +/** + * A class which manages pending restyles. This handles keeping track + * of what nodes restyles need to happen on and so forth. + */ + +#include "mozilla/dom/Element.h" +#include "nsDataHashtable.h" + +class nsCSSFrameConstructor; + +namespace mozilla { +namespace css { + +class RestyleTracker { +public: + typedef mozilla::dom::Element Element; + + RestyleTracker(PRUint32 aRestyleBits, + nsCSSFrameConstructor* aFrameConstructor) : + mRestyleBits(aRestyleBits), mFrameConstructor(aFrameConstructor) + { + NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0, + "Why do we have these bits set?"); + NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != 0, + "Must have a restyle flag"); + NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != + ELEMENT_PENDING_RESTYLE_FLAGS, + "Shouldn't have both restyle flags set"); + NS_PRECONDITION((mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS) != 0, + "Must have root flag"); + NS_PRECONDITION((mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS) != + (ELEMENT_ALL_RESTYLE_FLAGS & ~ELEMENT_PENDING_RESTYLE_FLAGS), + "Shouldn't have both root flags"); + } + + PRBool Init() { + return mPendingRestyles.Init(); + } + + PRUint32 Count() const { + return mPendingRestyles.Count(); + } + + /** + * Add a restyle for the given element to the tracker. + */ + inline void AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint); + + /** + * Process the restyles we've been tracking. + */ + void ProcessRestyles(); + + struct RestyleData { + nsRestyleHint mRestyleHint; // What we want to restyle + nsChangeHint mChangeHint; // The minimal change hint for "self" + }; + + struct RestyleEnumerateData : public RestyleData { + nsCOMPtr mElement; + }; + +private: + typedef nsDataHashtable PendingRestyleTable; + // Our restyle bits. These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and + // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS and one flag + // that's not in ELEMENT_PENDING_RESTYLE_FLAGS. + PRUint32 mRestyleBits; + nsCSSFrameConstructor* mFrameConstructor; // Owns us + PendingRestyleTable mPendingRestyles; +}; + +inline void RestyleTracker::AddPendingRestyle(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint) +{ + RestyleData existingData; + existingData.mRestyleHint = nsRestyleHint(0); + existingData.mChangeHint = NS_STYLE_HINT_NONE; + + mPendingRestyles.Get(aElement, &existingData); + + existingData.mRestyleHint = + nsRestyleHint(existingData.mRestyleHint | aRestyleHint); + NS_UpdateHint(existingData.mChangeHint, aMinChangeHint); + + mPendingRestyles.Put(aElement, existingData); +} + +} // namespace css +} // namespacs mozilla + +#endif /* mozilla_css_RestyleTracker_h */ diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index cec6f54d433c..1350bf43a476 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1397,6 +1397,10 @@ nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument, , mInLazyFCRefresh(PR_FALSE) , mHoverGeneration(0) , mRebuildAllExtraHint(nsChangeHint(0)) + , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE | + ELEMENT_IS_POTENTIAL_RESTYLE_ROOT, this) + , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE | + ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT, this) { // XXXbz this should be in Init() or something! if (!mPendingRestyles.Init() || !mPendingAnimationRestyles.Init()) { @@ -11589,26 +11593,6 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer, } -static PLDHashOperator -CollectRestyles(nsISupports* aElement, - nsCSSFrameConstructor::RestyleData& aData, - void* aRestyleArrayPtr) -{ - nsCSSFrameConstructor::RestyleEnumerateData** restyleArrayPtr = - static_cast - (aRestyleArrayPtr); - nsCSSFrameConstructor::RestyleEnumerateData* currentRestyle = - *restyleArrayPtr; - currentRestyle->mElement = static_cast(aElement); - currentRestyle->mRestyleHint = aData.mRestyleHint; - currentRestyle->mChangeHint = aData.mChangeHint; - - // Increment to the next slot in the array - *restyleArrayPtr = currentRestyle + 1; - - return PL_DHASH_NEXT; -} - void nsCSSFrameConstructor::ProcessOneRestyle(Element* aElement, nsRestyleHint aRestyleHint, @@ -11640,8 +11624,6 @@ nsCSSFrameConstructor::ProcessOneRestyle(Element* aElement, } } -#define RESTYLE_ARRAY_STACKSIZE 128 - void nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint) { @@ -11699,58 +11681,6 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint) batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC); } -void -nsCSSFrameConstructor::ProcessPendingRestyleTable( - nsDataHashtable& aRestyles) -{ - PRUint32 count = aRestyles.Count(); - - // Make sure to not rebuild quote or counter lists while we're - // processing restyles - BeginUpdate(); - - // loop so that we process any restyle events generated by processing - while (count) { - // Use the stack if we can, otherwise fall back on heap-allocation. - nsAutoTArray restyleArr; - RestyleEnumerateData* restylesToProcess = restyleArr.AppendElements(count); - - if (!restylesToProcess) { - return; - } - - RestyleEnumerateData* lastRestyle = restylesToProcess; - aRestyles.Enumerate(CollectRestyles, &lastRestyle); - - NS_ASSERTION(lastRestyle - restylesToProcess == PRInt32(count), - "Enumeration screwed up somehow"); - - // Clear the hashtable so we don't end up trying to process a restyle we're - // already processing, sending us into an infinite loop. - aRestyles.Clear(); - - for (RestyleEnumerateData* currentRestyle = restylesToProcess; - currentRestyle != lastRestyle; - ++currentRestyle) { - ProcessOneRestyle(currentRestyle->mElement, - currentRestyle->mRestyleHint, - currentRestyle->mChangeHint); - } - - count = aRestyles.Count(); - } - - // Set mInStyleRefresh to false now, since the EndUpdate call might - // add more restyles. - mInStyleRefresh = PR_FALSE; - - EndUpdate(); - -#ifdef DEBUG - mPresShell->VerifyStyleTree(); -#endif -} - void nsCSSFrameConstructor::ProcessPendingRestyles() { @@ -11764,7 +11694,7 @@ nsCSSFrameConstructor::ProcessPendingRestyles() "Nesting calls to ProcessPendingRestyles?"); presContext->SetProcessingRestyles(PR_TRUE); - ProcessPendingRestyleTable(mPendingRestyles); + mPendingRestyles.ProcessRestyles(); #ifdef DEBUG PRUint32 oldPendingRestyleCount = mPendingRestyles.Count(); @@ -11779,7 +11709,7 @@ nsCSSFrameConstructor::ProcessPendingRestyles() // the running transition so it can check for a new change on the same // property, and then posts an immediate animation style change). presContext->SetProcessingAnimationStyleChange(PR_TRUE); - ProcessPendingRestyleTable(mPendingAnimationRestyles); + mPendingAnimationRestyles.ProcessRestyles(); presContext->SetProcessingAnimationStyleChange(PR_FALSE); presContext->SetProcessingRestyles(PR_FALSE); @@ -11810,19 +11740,9 @@ nsCSSFrameConstructor::PostRestyleEventCommon(Element* aElement, return; } - RestyleData existingData; - existingData.mRestyleHint = nsRestyleHint(0); - existingData.mChangeHint = NS_STYLE_HINT_NONE; - - nsDataHashtable &restyles = + RestyleTracker& tracker = aForAnimation ? mPendingAnimationRestyles : mPendingRestyles; - - restyles.Get(aElement, &existingData); - existingData.mRestyleHint = - nsRestyleHint(existingData.mRestyleHint | aRestyleHint); - NS_UpdateHint(existingData.mChangeHint, aMinChangeHint); - - restyles.Put(aElement, existingData); + tracker.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint); PostRestyleEventInternal(PR_FALSE); } diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index b2b4d1e14d59..fc139e134462 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -48,12 +48,12 @@ #include "nsIXBLService.h" #include "nsQuoteList.h" #include "nsCounterManager.h" -#include "nsDataHashtable.h" #include "nsHashKeys.h" #include "nsThreadUtils.h" #include "nsPageContentFrame.h" #include "nsCSSPseudoElements.h" #include "nsRefreshDriver.h" +#include "RestyleTracker.h" class nsIDocument; struct nsFrameItems; @@ -83,6 +83,7 @@ class nsCSSFrameConstructor : public nsARefreshObserver { public: typedef mozilla::dom::Element Element; + typedef mozilla::css::RestyleTracker RestyleTracker; nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell); ~nsCSSFrameConstructor(void) { @@ -307,13 +308,11 @@ private: // This function does not call ProcessAttachedQueue() on the binding manager. // If the caller wants that to happen synchronously, it needs to handle that // itself. + friend class mozilla::css::RestyleTracker; void ProcessOneRestyle(Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aChangeHint); - void ProcessPendingRestyleTable( - nsDataHashtable& aRestyles); - void RestyleForEmptyChange(Element* aContainer); public: @@ -1837,15 +1836,6 @@ private: public: - struct RestyleData { - nsRestyleHint mRestyleHint; // What we want to restyle - nsChangeHint mChangeHint; // The minimal change hint for "self" - }; - - struct RestyleEnumerateData : public RestyleData { - nsCOMPtr mElement; - }; - friend class nsFrameConstructorState; private: @@ -1910,8 +1900,8 @@ private: nsCOMPtr mTempFrameTreeState; - nsDataHashtable mPendingRestyles; - nsDataHashtable mPendingAnimationRestyles; + RestyleTracker mPendingRestyles; + RestyleTracker mPendingAnimationRestyles; static nsIXBLService * gXBLService; }; From ecd2c795509cbb2db8a655863fea99192d46411d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 06/17] Bug 479655 part 4. Move handling of eRestyle_LaterSiblings into RestyleTracker. r=dbaron --- layout/base/RestyleTracker.cpp | 45 ++++++++++++++++++++++++--- layout/base/RestyleTracker.h | 5 +++ layout/base/nsCSSFrameConstructor.cpp | 45 --------------------------- layout/base/nsCSSFrameConstructor.h | 10 ------ 4 files changed, 46 insertions(+), 59 deletions(-) diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp index 151dbc44dfcc..5f8b5f9111e5 100644 --- a/layout/base/RestyleTracker.cpp +++ b/layout/base/RestyleTracker.cpp @@ -38,6 +38,7 @@ #include "RestyleTracker.h" #include "nsCSSFrameConstructor.h" +#include "nsStyleChangeList.h" namespace mozilla { namespace css { @@ -64,6 +65,43 @@ CollectRestyles(nsISupports* aElement, return PL_DHASH_NEXT; } +inline void +RestyleTracker::ProcessOneRestyle(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aChangeHint) +{ + if (aElement->GetCurrentDoc() != mFrameConstructor->mDocument) { + // Content node has been removed from our document; nothing else + // to do here + return; + } + + nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); + if (aRestyleHint & eRestyle_Self) { + mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint); + } else if (aChangeHint && + (primaryFrame || + (aChangeHint & nsChangeHint_ReconstructFrame))) { + // Don't need to recompute style; just apply the hint + nsStyleChangeList changeList; + changeList.AppendChange(primaryFrame, aElement, aChangeHint); + mFrameConstructor->ProcessRestyledFrames(changeList); + } + + if (aRestyleHint & eRestyle_LaterSiblings) { + for (nsIContent* sibling = aElement->GetNextSibling(); + sibling; + sibling = sibling->GetNextSibling()) { + if (!sibling->IsElement()) + continue; + + mFrameConstructor->RestyleElement(sibling->AsElement(), + sibling->GetPrimaryFrame(), + NS_STYLE_HINT_NONE); + } + } +} + void RestyleTracker::ProcessRestyles() { @@ -96,9 +134,9 @@ RestyleTracker::ProcessRestyles() for (RestyleEnumerateData* currentRestyle = restylesToProcess; currentRestyle != lastRestyle; ++currentRestyle) { - mFrameConstructor->ProcessOneRestyle(currentRestyle->mElement, - currentRestyle->mRestyleHint, - currentRestyle->mChangeHint); + ProcessOneRestyle(currentRestyle->mElement, + currentRestyle->mRestyleHint, + currentRestyle->mChangeHint); } count = mPendingRestyles.Count(); @@ -113,7 +151,6 @@ RestyleTracker::ProcessRestyles() #ifdef DEBUG mFrameConstructor->mPresShell->VerifyStyleTree(); #endif - } } // namespace css diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index e6cc294f7f76..3f21f860c649 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -102,6 +102,11 @@ public: }; private: + // Handle a single mPendingRestyles entry. + inline void ProcessOneRestyle(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aChangeHint); + typedef nsDataHashtable PendingRestyleTable; // Our restyle bits. These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS and one flag diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 1350bf43a476..7aa8fda00a64 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8108,20 +8108,6 @@ nsCSSFrameConstructor::RestyleElement(Element *aElement, } } -void -nsCSSFrameConstructor::RestyleLaterSiblings(Element *aElement) -{ - for (nsIContent* sibling = aElement->GetNextSibling(); - sibling; - sibling = sibling->GetNextSibling()) { - if (!sibling->IsElement()) - continue; - - RestyleElement(sibling->AsElement(), sibling->GetPrimaryFrame(), - NS_STYLE_HINT_NONE); - } -} - nsresult nsCSSFrameConstructor::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2, @@ -11593,37 +11579,6 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer, } -void -nsCSSFrameConstructor::ProcessOneRestyle(Element* aElement, - nsRestyleHint aRestyleHint, - nsChangeHint aChangeHint) -{ - NS_PRECONDITION(aElement, "Must have element"); - - if (!aElement->IsInDoc() || - aElement->GetCurrentDoc() != mDocument) { - // Content node has been removed from our document; nothing else - // to do here - return; - } - - nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); - if (aRestyleHint & eRestyle_Self) { - RestyleElement(aElement, primaryFrame, aChangeHint); - } else if (aChangeHint && - (primaryFrame || - (aChangeHint & nsChangeHint_ReconstructFrame))) { - // Don't need to recompute style; just apply the hint - nsStyleChangeList changeList; - changeList.AppendChange(primaryFrame, aElement, aChangeHint); - ProcessRestyledFrames(changeList); - } - - if (aRestyleHint & eRestyle_LaterSiblings) { - RestyleLaterSiblings(aElement); - } -} - void nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint) { diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index fc139e134462..00cd231c4751 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -303,15 +303,7 @@ public: private: - // Note: It's the caller's responsibility to make sure to wrap a - // ProcessOneRestyle call in a view update batch. - // This function does not call ProcessAttachedQueue() on the binding manager. - // If the caller wants that to happen synchronously, it needs to handle that - // itself. friend class mozilla::css::RestyleTracker; - void ProcessOneRestyle(Element* aElement, - nsRestyleHint aRestyleHint, - nsChangeHint aChangeHint); void RestyleForEmptyChange(Element* aContainer); @@ -462,8 +454,6 @@ private: nsIFrame* aPrimaryFrame, nsChangeHint aMinHint); - void RestyleLaterSiblings(Element* aElement); - nsresult InitAndRestoreFrame (const nsFrameConstructorState& aState, nsIContent* aContent, nsIFrame* aParentFrame, From ef0bbab82b62541d0efdeac6bebec6dd2f453c14 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 07/17] Bug 479655 part 5. Pass the relevant restyle tracker through to style reresolution. r=dbaron --- layout/base/RestyleTracker.cpp | 6 ++++-- layout/base/nsCSSFrameConstructor.cpp | 10 +++++++--- layout/base/nsCSSFrameConstructor.h | 3 ++- layout/base/nsFrameManager.cpp | 22 +++++++++++++++------- layout/base/nsFrameManager.h | 14 ++++++++++++-- 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp index 5f8b5f9111e5..64c7e71be5c3 100644 --- a/layout/base/RestyleTracker.cpp +++ b/layout/base/RestyleTracker.cpp @@ -78,7 +78,8 @@ RestyleTracker::ProcessOneRestyle(Element* aElement, nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); if (aRestyleHint & eRestyle_Self) { - mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint); + mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint, + *this); } else if (aChangeHint && (primaryFrame || (aChangeHint & nsChangeHint_ReconstructFrame))) { @@ -97,7 +98,8 @@ RestyleTracker::ProcessOneRestyle(Element* aElement, mFrameConstructor->RestyleElement(sibling->AsElement(), sibling->GetPrimaryFrame(), - NS_STYLE_HINT_NONE); + NS_STYLE_HINT_NONE, + *this); } } } diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 7aa8fda00a64..fc149164c7ce 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8083,7 +8083,8 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList) void nsCSSFrameConstructor::RestyleElement(Element *aElement, nsIFrame *aPrimaryFrame, - nsChangeHint aMinHint) + nsChangeHint aMinHint, + RestyleTracker& aRestyleTracker) { NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(), "frame/content mismatch"); @@ -8100,7 +8101,8 @@ nsCSSFrameConstructor::RestyleElement(Element *aElement, } else if (aPrimaryFrame) { nsStyleChangeList changeList; mPresShell->FrameManager()-> - ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint); + ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint, + aRestyleTracker); ProcessRestyledFrames(changeList); } else { // no frames, reconstruct for content @@ -11623,8 +11625,10 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint) nsStyleChangeList changeList; // XXX Does it matter that we're passing aExtraHint to the real root // frame and not the root node's primary frame? + // Note: The restyle tracker we pass in here doesn't matter. mPresShell->FrameManager()->ComputeStyleChangeFor(mPresShell->GetRootFrame(), - &changeList, aExtraHint); + &changeList, aExtraHint, + mPendingRestyles); // Process the required changes ProcessRestyledFrames(changeList); // Tell the style set it's safe to destroy the old rule tree. We diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 00cd231c4751..154f1b5ee746 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -452,7 +452,8 @@ private: // XXXbz do we really need the aPrimaryFrame argument here? void RestyleElement(Element* aElement, nsIFrame* aPrimaryFrame, - nsChangeHint aMinHint); + nsChangeHint aMinHint, + RestyleTracker& aRestyleTracker); nsresult InitAndRestoreFrame (const nsFrameConstructorState& aState, nsIContent* aContent, diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 31660454d330..92d3903c58dd 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -91,6 +91,7 @@ #include "nsAutoPtr.h" #include "imgIRequest.h" #include "nsTransitionManager.h" +#include "RestyleTracker.h" #include "nsFrameManager.h" #ifdef ACCESSIBILITY @@ -961,7 +962,8 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, nsIContent *aParentContent, nsStyleChangeList *aChangeList, nsChangeHint aMinChange, - PRBool aFireAccessibilityEvents) + PRBool aFireAccessibilityEvents, + RestyleTracker& aRestyleTracker) { if (!NS_IsHintSubset(nsChangeHint_NeedDirtyReflow, aMinChange)) { // If aMinChange doesn't include nsChangeHint_NeedDirtyReflow, clear out @@ -1049,7 +1051,8 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // frame because it is visible or hidden withitn this frame. assumeDifferenceHint = ReResolveStyleContext(aPresContext, providerFrame, aParentContent, aChangeList, - aMinChange, PR_FALSE); + aMinChange, PR_FALSE, + aRestyleTracker); // The provider's new context becomes the parent context of // aFrame's context. @@ -1390,19 +1393,22 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, content, aChangeList, NS_SubtractHint(aMinChange, nsChangeHint_ReflowFrame), - fireAccessibilityEvents); + fireAccessibilityEvents, + aRestyleTracker); // reresolve placeholder's context under the same parent // as the out-of-flow frame ReResolveStyleContext(aPresContext, child, content, aChangeList, aMinChange, - fireAccessibilityEvents); + fireAccessibilityEvents, + aRestyleTracker); } else { // regular child frame if (child != resolvedChild) { ReResolveStyleContext(aPresContext, child, content, aChangeList, aMinChange, - fireAccessibilityEvents); + fireAccessibilityEvents, + aRestyleTracker); } else { NOISY_TRACE_FRAME("child frame already resolved as descendant, skipping",aFrame); } @@ -1424,7 +1430,8 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, void nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame, nsStyleChangeList *aChangeList, - nsChangeHint aMinChange) + nsChangeHint aMinChange, + RestyleTracker& aRestyleTracker) { if (aMinChange) { aChangeList->AppendChange(aFrame, aFrame->GetContent(), aMinChange); @@ -1449,7 +1456,8 @@ nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame, // Inner loop over next-in-flows of the current frame nsChangeHint frameChange = ReResolveStyleContext(GetPresContext(), frame, nsnull, - aChangeList, topLevelChange, PR_TRUE); + aChangeList, topLevelChange, PR_TRUE, + aRestyleTracker); NS_UpdateHint(topLevelChange, frameChange); if (topLevelChange & nsChangeHint_ReconstructFrame) { diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h index ae32641e30c5..3087a56efee1 100644 --- a/layout/base/nsFrameManager.h +++ b/layout/base/nsFrameManager.h @@ -57,6 +57,12 @@ #include "nsChangeHint.h" #include "nsFrameManagerBase.h" +namespace mozilla { +namespace css { +class RestyleTracker; +} // namespace css +} // namespace mozilla + /** * Frame manager interface. The frame manager serves two purposes: *
  • provides a service for mapping from content to frame and from @@ -70,6 +76,8 @@ class nsFrameManager : public nsFrameManagerBase { + typedef mozilla::css::RestyleTracker RestyleTracker; + public: nsFrameManager() NS_HIDDEN; ~nsFrameManager() NS_HIDDEN; @@ -151,7 +159,8 @@ public: NS_HIDDEN_(void) ComputeStyleChangeFor(nsIFrame* aFrame, nsStyleChangeList* aChangeList, - nsChangeHint aMinChange); + nsChangeHint aMinChange, + RestyleTracker& aRestyleTracker); /* * Capture/restore frame state for the frame subtree rooted at aFrame. @@ -198,7 +207,8 @@ private: nsIContent *aParentContent, nsStyleChangeList *aChangeList, nsChangeHint aMinChange, - PRBool aFireAccessibilityEvents); + PRBool aFireAccessibilityEvents, + RestyleTracker& aRestyleTracker); }; #endif From 9fd308fab42a05ce597cdc35977125dcec39d9fc Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 08/17] Bug 479655 part 6. Add a private AddPendingRestyle API that allows removing restyle hints, as well as adding them. r=dbaron --- layout/base/RestyleTracker.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index 3f21f860c649..67dd85a58ccf 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -84,8 +84,10 @@ public: /** * Add a restyle for the given element to the tracker. */ - inline void AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, - nsChangeHint aMinChangeHint); + void AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint) { + AddPendingRestyle(aElement, aRestyleHint, nsRestyleHint(0), aMinChangeHint); + } /** * Process the restyles we've been tracking. @@ -102,6 +104,15 @@ public: }; private: + /** + * Add a restyle for the given element to the tracker and remove the + * given bits from the existing restyle hint for the element, if + * there is one. + */ + inline void AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, + nsRestyleHint aRestyleHintToRemove, + nsChangeHint aMinChangeHint); + // Handle a single mPendingRestyles entry. inline void ProcessOneRestyle(Element* aElement, nsRestyleHint aRestyleHint, @@ -118,6 +129,7 @@ private: inline void RestyleTracker::AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, + nsRestyleHint aRestyleHintToRemove, nsChangeHint aMinChangeHint) { RestyleData existingData; @@ -127,7 +139,8 @@ inline void RestyleTracker::AddPendingRestyle(Element* aElement, mPendingRestyles.Get(aElement, &existingData); existingData.mRestyleHint = - nsRestyleHint(existingData.mRestyleHint | aRestyleHint); + nsRestyleHint((existingData.mRestyleHint | aRestyleHint) & + ~aRestyleHintToRemove); NS_UpdateHint(existingData.mChangeHint, aMinChangeHint); mPendingRestyles.Put(aElement, existingData); From 01c5641b98fbc3c01c5199d560b7842d8f460dc4 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 09/17] Bug 479655 part 7. Switch to the new setup for tracking and handling restyles. r=dbaron --- layout/base/RestyleTracker.cpp | 255 +++++++++++++++++++++----- layout/base/RestyleTracker.h | 129 ++++++++++--- layout/base/nsCSSFrameConstructor.cpp | 46 ++++- layout/base/nsFrameManager.cpp | 19 +- 4 files changed, 370 insertions(+), 79 deletions(-) diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp index 64c7e71be5c3..eaa128013b27 100644 --- a/layout/base/RestyleTracker.cpp +++ b/layout/base/RestyleTracker.cpp @@ -43,19 +43,86 @@ namespace mozilla { namespace css { +inline nsIDocument* +RestyleTracker::Document() const { + return mFrameConstructor->mDocument; +} + #define RESTYLE_ARRAY_STACKSIZE 128 +struct LaterSiblingCollector { + RestyleTracker* tracker; + nsTArray< nsRefPtr >* elements; +}; + +static PLDHashOperator +CollectLaterSiblings(nsISupports* aElement, + RestyleTracker::RestyleData& aData, + void* aSiblingCollector) +{ + dom::Element* element = + static_cast(aElement); + LaterSiblingCollector* collector = + static_cast(aSiblingCollector); + // Only collect the entries that actually need restyling by us (and + // haven't, for example, already been restyled). + // It's important to not mess with the flags on entries not in our + // document. + if (element->GetCurrentDoc() == collector->tracker->Document() && + element->HasFlag(collector->tracker->RestyleBit()) && + (aData.mRestyleHint & eRestyle_LaterSiblings)) { + collector->elements->AppendElement(element); + } + + return PL_DHASH_NEXT; +} + +struct RestyleCollector { + RestyleTracker* tracker; + RestyleTracker::RestyleEnumerateData** restyleArrayPtr; +}; + static PLDHashOperator CollectRestyles(nsISupports* aElement, RestyleTracker::RestyleData& aData, - void* aRestyleArrayPtr) + void* aRestyleCollector) { + dom::Element* element = + static_cast(aElement); + RestyleCollector* collector = + static_cast(aRestyleCollector); + // Only collect the entries that actually need restyling by us (and + // haven't, for example, already been restyled). + // It's important to not mess with the flags on entries not in our + // document. + if (element->GetCurrentDoc() != collector->tracker->Document() || + !element->HasFlag(collector->tracker->RestyleBit())) { + return PL_DHASH_NEXT; + } + + NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) || + // Maybe we're just not reachable via the frame tree? + (element->GetFlattenedTreeParent() && + !element->GetFlattenedTreeParent()->GetPrimaryFrame()) || + // Or not reachable due to an async reinsert we have + // pending? If so, we'll have a reframe hint around. + // That incidentally makes it safe that we still have + // the bit, since any descendants that didn't get added + // to the roots list because we had the bits will be + // completely restyled in a moment. + (aData.mChangeHint & nsChangeHint_ReconstructFrame), + "Why did this not get handled while processing mRestyleRoots?"); + + // Unset the restyle bits now, so if they get readded later as we + // process we won't clobber that adding of the bit. + element->UnsetFlags(collector->tracker->RestyleBit() | + collector->tracker->RootBit()); + RestyleTracker::RestyleEnumerateData** restyleArrayPtr = - static_cast - (aRestyleArrayPtr); + collector->restyleArrayPtr; RestyleTracker::RestyleEnumerateData* currentRestyle = *restyleArrayPtr; - currentRestyle->mElement = static_cast(aElement); + currentRestyle->mElement = element; currentRestyle->mRestyleHint = aData.mRestyleHint; currentRestyle->mChangeHint = aData.mChangeHint; @@ -70,11 +137,10 @@ RestyleTracker::ProcessOneRestyle(Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aChangeHint) { - if (aElement->GetCurrentDoc() != mFrameConstructor->mDocument) { - // Content node has been removed from our document; nothing else - // to do here - return; - } + NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0, + "Someone should have handled this before calling us"); + NS_PRECONDITION(aElement->GetCurrentDoc() == mFrameConstructor->mDocument, + "Element has unexpected document"); nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); if (aRestyleHint & eRestyle_Self) { @@ -88,60 +154,111 @@ RestyleTracker::ProcessOneRestyle(Element* aElement, changeList.AppendChange(primaryFrame, aElement, aChangeHint); mFrameConstructor->ProcessRestyledFrames(changeList); } - - if (aRestyleHint & eRestyle_LaterSiblings) { - for (nsIContent* sibling = aElement->GetNextSibling(); - sibling; - sibling = sibling->GetNextSibling()) { - if (!sibling->IsElement()) - continue; - - mFrameConstructor->RestyleElement(sibling->AsElement(), - sibling->GetPrimaryFrame(), - NS_STYLE_HINT_NONE, - *this); - } - } } void RestyleTracker::ProcessRestyles() { - PRUint32 count = mPendingRestyles.Count(); - // Make sure to not rebuild quote or counter lists while we're // processing restyles mFrameConstructor->BeginUpdate(); // loop so that we process any restyle events generated by processing - while (count) { - // Use the stack if we can, otherwise fall back on heap-allocation. + while (mPendingRestyles.Count()) { + if (mHaveLaterSiblingRestyles) { + // Convert them to individual restyles on all the later siblings + nsAutoTArray, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr; + LaterSiblingCollector siblingCollector = { this, &laterSiblingArr }; + mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector); + for (PRUint32 i = 0; i < laterSiblingArr.Length(); ++i) { + Element* element = laterSiblingArr[i]; + for (nsIContent* sibling = element->GetNextSibling(); + sibling; + sibling = sibling->GetNextSibling()) { + if (sibling->IsElement() && + AddPendingRestyle(sibling->AsElement(), eRestyle_Self, + NS_STYLE_HINT_NONE)) { + // Nothing else to do here; we'll handle the following + // siblings when we get to |sibling| in laterSiblingArr. + break; + } + } + } + + // Now remove all those eRestyle_LaterSiblings bits + for (PRUint32 i = 0; i < laterSiblingArr.Length(); ++i) { + Element* element = laterSiblingArr[i]; + NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?"); + RestyleData data; +#ifdef DEBUG + PRBool found = +#endif + mPendingRestyles.Get(element, &data); + NS_ASSERTION(found, "Where did our entry go?"); + data.mRestyleHint = + nsRestyleHint(data.mRestyleHint & ~eRestyle_LaterSiblings); + + mPendingRestyles.Put(element, data); + } + + mHaveLaterSiblingRestyles = PR_FALSE; + } + + PRUint32 rootCount; + while ((rootCount = mRestyleRoots.Length())) { + // Make sure to pop the element off our restyle root array, so + // that we can freely append to the array as we process this + // element. + nsRefPtr element; + element.swap(mRestyleRoots[rootCount - 1]); + mRestyleRoots.RemoveElementAt(rootCount - 1); + + // Do the document check before calling GetRestyleData, since we + // don't want to do the sibling-processing GetRestyleData does if + // the node is no longer relevant. + if (element->GetCurrentDoc() != mFrameConstructor->mDocument) { + // Content node has been removed from our document; nothing else + // to do here + continue; + } + + RestyleData data; + if (!GetRestyleData(element, &data)) { + continue; + } + + ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint); + } + + if (mHaveLaterSiblingRestyles) { + // Keep processing restyles for now + continue; + } + + // Now we only have entries with change hints left. To be safe in + // case of reentry from the handing of the change hint, use a + // scratch array instead of calling out to ProcessOneRestyle while + // enumerating the hashtable. Use the stack if we can, otherwise + // fall back on heap-allocation. nsAutoTArray restyleArr; - RestyleEnumerateData* restylesToProcess = restyleArr.AppendElements(count); + RestyleEnumerateData* restylesToProcess = + restyleArr.AppendElements(mPendingRestyles.Count()); + if (restylesToProcess) { + RestyleEnumerateData* lastRestyle = restylesToProcess; + RestyleCollector collector = { this, &lastRestyle }; + mPendingRestyles.Enumerate(CollectRestyles, &collector); - if (!restylesToProcess) { - return; + // Clear the hashtable now that we don't need it anymore + mPendingRestyles.Clear(); + + for (RestyleEnumerateData* currentRestyle = restylesToProcess; + currentRestyle != lastRestyle; + ++currentRestyle) { + ProcessOneRestyle(currentRestyle->mElement, + currentRestyle->mRestyleHint, + currentRestyle->mChangeHint); + } } - - RestyleEnumerateData* lastRestyle = restylesToProcess; - mPendingRestyles.Enumerate(CollectRestyles, &lastRestyle); - - NS_ASSERTION(lastRestyle - restylesToProcess == PRInt32(count), - "Enumeration screwed up somehow"); - - // Clear the hashtable so we don't end up trying to process a restyle we're - // already processing, sending us into an infinite loop. - mPendingRestyles.Clear(); - - for (RestyleEnumerateData* currentRestyle = restylesToProcess; - currentRestyle != lastRestyle; - ++currentRestyle) { - ProcessOneRestyle(currentRestyle->mElement, - currentRestyle->mRestyleHint, - currentRestyle->mChangeHint); - } - - count = mPendingRestyles.Count(); } // Set mInStyleRefresh to false now, since the EndUpdate call might @@ -155,5 +272,43 @@ RestyleTracker::ProcessRestyles() #endif } +PRBool +RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData) +{ + NS_PRECONDITION(aElement->GetCurrentDoc() == mFrameConstructor->mDocument, + "Unexpected document; this will lead to incorrect behavior!"); + + if (!aElement->HasFlag(RestyleBit())) { + NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?"); + return PR_FALSE; + } + +#ifdef DEBUG + PRBool gotData = +#endif + mPendingRestyles.Get(aElement, aData); + NS_ASSERTION(gotData, "Must have data if restyle bit is set"); + + if (aData->mRestyleHint & eRestyle_LaterSiblings) { + // Someone readded the eRestyle_LaterSiblings hint for this + // element. Leave it around for now, but remove the other restyle + // hints and the change hint for it. Also unset its root bit, + // since it's no longer a root with the new restyle data. + RestyleData newData; + newData.mChangeHint = nsChangeHint(0); + newData.mRestyleHint = eRestyle_LaterSiblings; + mPendingRestyles.Put(aElement, newData); + aElement->UnsetFlags(RootBit()); + aData->mRestyleHint = + nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings); + } else { + mPendingRestyles.Remove(aElement); + aElement->UnsetFlags(mRestyleBits); + } + + return PR_TRUE; +} + } // namespace css } // namespace mozilla + diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index 67dd85a58ccf..7e44c5185c5f 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -45,6 +45,7 @@ #include "mozilla/dom/Element.h" #include "nsDataHashtable.h" +#include "nsIFrame.h" class nsCSSFrameConstructor; @@ -57,7 +58,8 @@ public: RestyleTracker(PRUint32 aRestyleBits, nsCSSFrameConstructor* aFrameConstructor) : - mRestyleBits(aRestyleBits), mFrameConstructor(aFrameConstructor) + mRestyleBits(aRestyleBits), mFrameConstructor(aFrameConstructor), + mHaveLaterSiblingRestyles(PR_FALSE) { NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0, "Why do we have these bits set?"); @@ -82,68 +84,151 @@ public: } /** - * Add a restyle for the given element to the tracker. + * Add a restyle for the given element to the tracker. Returns true + * if the element already had eRestyle_LaterSiblings set on it. */ - void AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, - nsChangeHint aMinChangeHint) { - AddPendingRestyle(aElement, aRestyleHint, nsRestyleHint(0), aMinChangeHint); - } + PRBool AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint); /** * Process the restyles we've been tracking. */ void ProcessRestyles(); + // Return our ELEMENT_HAS_PENDING_(ANIMATION_)RESTYLE bit + PRUint32 RestyleBit() const { + return mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS; + } + + // Return our ELEMENT_IS_POTENTIAL_(ANIMATION_)RESTYLE_ROOT bit + PRUint32 RootBit() const { + return mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS; + } + struct RestyleData { nsRestyleHint mRestyleHint; // What we want to restyle nsChangeHint mChangeHint; // The minimal change hint for "self" }; + /** + * If the given Element has a restyle pending for it, return the + * relevant restyle data. This function will clear everything other + * than a possible eRestyle_LaterSiblings hint for aElement out of + * our hashtable. The returned aData will never have an + * eRestyle_LaterSiblings hint in it. + * + * The return value indicates whether any restyle data was found for + * the element. If false is returned, then the state of *aData is + * undefined. + */ + PRBool GetRestyleData(Element* aElement, RestyleData* aData); + + /** + * The document we're associated with. + */ + inline nsIDocument* Document() const; + struct RestyleEnumerateData : public RestyleData { nsCOMPtr mElement; }; private: /** - * Add a restyle for the given element to the tracker and remove the - * given bits from the existing restyle hint for the element, if - * there is one. + * Handle a single mPendingRestyles entry. aRestyleHint must not + * include eRestyle_LaterSiblings; that needs to be dealt with + * before calling this function. */ - inline void AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, - nsRestyleHint aRestyleHintToRemove, - nsChangeHint aMinChangeHint); - - // Handle a single mPendingRestyles entry. inline void ProcessOneRestyle(Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aChangeHint); - + typedef nsDataHashtable PendingRestyleTable; + typedef nsAutoTArray< nsRefPtr, 32> RestyleRootArray; // Our restyle bits. These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS and one flag // that's not in ELEMENT_PENDING_RESTYLE_FLAGS. PRUint32 mRestyleBits; nsCSSFrameConstructor* mFrameConstructor; // Owns us + // A hashtable that maps elements to RestyleData structs. The + // values only make sense if the element's current document is our + // document and it has our RestyleBit() flag set. In particular, + // said bit might not be set if the element had a restyle posted and + // then was moved around in the DOM. PendingRestyleTable mPendingRestyles; + // An array that keeps track of our possible restyle roots. This + // maintains the invariant that if A and B are both restyle roots + // and A is an ancestor of B then A will come after B in the array. + // We maintain this invariant by checking whether an element has an + // ancestor with the restyle root bit set before appending it to the + // array. + RestyleRootArray mRestyleRoots; + // True if we have some entries with the eRestyle_LaterSiblings + // flag. We need this to avoid enumerating the hashtable looking + // for such entries when we can't possibly have any. + PRBool mHaveLaterSiblingRestyles; }; -inline void RestyleTracker::AddPendingRestyle(Element* aElement, - nsRestyleHint aRestyleHint, - nsRestyleHint aRestyleHintToRemove, - nsChangeHint aMinChangeHint) +inline PRBool RestyleTracker::AddPendingRestyle(Element* aElement, + nsRestyleHint aRestyleHint, + nsChangeHint aMinChangeHint) { RestyleData existingData; existingData.mRestyleHint = nsRestyleHint(0); existingData.mChangeHint = NS_STYLE_HINT_NONE; - mPendingRestyles.Get(aElement, &existingData); + // Check the RestyleBit() flag before doing the hashtable Get, since + // it's possible that the data in the hashtable isn't actually + // relevant anymore (if the flag is not set). + if (aElement->HasFlag(RestyleBit())) { + mPendingRestyles.Get(aElement, &existingData); + } else { + aElement->SetFlags(RestyleBit()); + } + PRBool hadRestyleLaterSiblings = + (existingData.mRestyleHint & eRestyle_LaterSiblings) != 0; existingData.mRestyleHint = - nsRestyleHint((existingData.mRestyleHint | aRestyleHint) & - ~aRestyleHintToRemove); + nsRestyleHint(existingData.mRestyleHint | aRestyleHint); NS_UpdateHint(existingData.mChangeHint, aMinChangeHint); mPendingRestyles.Put(aElement, existingData); + + // We can only treat this element as a restyle root if we would + // actually restyle its descendants (so either call + // ReResolveStyleContext on it or just reframe it). + if ((aRestyleHint & eRestyle_Self) || + (aMinChangeHint & nsChangeHint_ReconstructFrame)) { + for (const Element* cur = aElement; !cur->HasFlag(RootBit()); ) { + nsIContent* parent = cur->GetFlattenedTreeParent(); + // Stop if we have no parent or the parent is not an element or + // we're part of the viewport scrollbars (because those are not + // frametree descendants of the primary frame of the root + // element). + // XXXbz maybe the primary frame of the root should be the root scrollframe? + if (!parent || !parent->IsElement() || + // If we've hit the root via a native anonymous kid and that + // this native anonymous kid is not obviously a descendant + // of the root's primary frame, assume we're under the root + // scrollbars. Since those don't get reresolved when + // reresolving the root, we need to make sure to add the + // element to mRestyleRoots. + (cur->IsInNativeAnonymousSubtree() && !parent->GetParent() && + cur->GetPrimaryFrame() && + cur->GetPrimaryFrame()->GetParent() != parent->GetPrimaryFrame())) { + mRestyleRoots.AppendElement(aElement); + break; + } + cur = parent->AsElement(); + } + // At this point some ancestor of aElement (possibly aElement + // itself) is in mRestyleRoots. Set the root bit on aElement, to + // speed up searching for an existing root on its descendants. + aElement->SetFlags(RootBit()); + } + + mHaveLaterSiblingRestyles = + mHaveLaterSiblingRestyles || (aRestyleHint & eRestyle_LaterSiblings) != 0; + return hadRestyleLaterSiblings; } } // namespace css diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index fc149164c7ce..e8e226024499 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2337,6 +2337,14 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle if (!mTempFrameTreeState) state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState)); + // Make sure that we'll handle restyles for this document element in + // the future. We need this, because the document element might + // have stale restyle bits from a previous frame constructor for + // this document. Unlike in AddFrameConstructionItems, it's safe to + // unset all element restyle flags, since we don't have any + // siblings. + aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); + // --------- CREATE AREA OR BOX FRAME ------- nsRefPtr styleContext; styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement, @@ -5028,6 +5036,16 @@ nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState FrameConstructionItemList& aItems) { aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); + if (aContent->IsElement()) { + // We can't just remove our pending restyle flags, since we may + // have restyle-later-siblings set on us. But we _can_ remove the + // "is possible restyle root" flags, and need to. Otherwise we can + // end up with stale such flags (e.g. if we used to have a + // display:none parent when our last restyle was posted and + // processed and now no longer do). + aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS & + ~ELEMENT_PENDING_RESTYLE_FLAGS); + } // don't create a whitespace frame if aParent doesn't want it if (!NeedFrameFor(aState, aParentFrame, aContent)) { @@ -9625,7 +9643,13 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState, iter != last; ++iter) { PRInt32 i = iter.XBLInvolved() ? -1 : iter.position(); - AddFrameConstructionItems(aState, *iter, i, aFrame, itemsToConstruct); + nsIContent* child = *iter; + // Frame construction item construction should not post + // restyles, so removing restyle flags here is safe. + if (child->IsElement()) { + child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); + } + AddFrameConstructionItems(aState, child, i, aFrame, itemsToConstruct); } itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved()); @@ -10949,6 +10973,13 @@ nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState, content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { continue; } + if (content->IsElement()) { + // See comment explaining why we need to remove the "is possible + // restyle root" flags in AddFrameConstructionItems. But note + // that we can remove all restyle flags, just like in + // ProcessChildren and for the same reason. + content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); + } nsRefPtr childContext = ResolveStyleContext(parentStyleContext, content); @@ -11402,11 +11433,14 @@ static void RestyleSiblingsStartingWith(nsCSSFrameConstructor *aFrameConstructor, nsIContent *aStartingSibling /* may be null */) { - if (aStartingSibling) { - nsIContent* parent = aStartingSibling->GetParent(); - if (parent && parent->IsElement()) { - aFrameConstructor->PostRestyleEvent(parent->AsElement(), eRestyle_Self, - NS_STYLE_HINT_NONE); + for (nsIContent *sibling = aStartingSibling; sibling; + sibling = sibling->GetNextSibling()) { + if (sibling->IsElement()) { + aFrameConstructor-> + PostRestyleEvent(sibling->AsElement(), + nsRestyleHint(eRestyle_Self | eRestyle_LaterSiblings), + NS_STYLE_HINT_NONE); + break; } } } diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 92d3903c58dd..7007bb0edd7b 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -982,7 +982,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, aMinChange = NS_SubtractHint(aMinChange, nsChangeHint_ClearAncestorIntrinsics); } - + // It would be nice if we could make stronger assertions here; they // would let us simplify the ?: expressions below setting |content| // and |pseudoContent| in sensible ways as well as making what @@ -1023,6 +1023,15 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // comment above assertion at start of function.) nsIContent* content = localContent ? localContent : aParentContent; + if (content && content->IsElement()) { + RestyleTracker::RestyleData restyleData; + if (aRestyleTracker.GetRestyleData(content->AsElement(), &restyleData)) { + if (NS_UpdateHint(aMinChange, restyleData.mChangeHint)) { + aChangeList->AppendChange(aFrame, content, restyleData.mChangeHint); + } + } + } + nsStyleContext* parentContext; nsIFrame* resolvedChild = nsnull; // Get the frame providing the parent style context. If it is a @@ -1256,6 +1265,14 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, NS_ASSERTION(!undisplayed->mStyle->GetPseudo(), "Shouldn't have random pseudo style contexts in the " "undisplayed map"); + RestyleTracker::RestyleData undisplayedRestyleData; + if (aRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(), + &undisplayedRestyleData)) { + // Nothing to do with it for now; when we don't + // automatically restyle our kids we'll need to handle that + // here. We do want the GetRestyleData call, though, to + // preserve the restyle tracker's invariants. + } nsRefPtr undisplayedContext = styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(), newContext); if (undisplayedContext) { From 43f065f6aec756505fce25abff72dd108d2fb94b Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 10/17] Bug 494117 part 1. Rename eRestyle_Self to eRestyle_Subtree. r=dbaron --- layout/base/RestyleTracker.cpp | 4 ++-- layout/base/RestyleTracker.h | 2 +- layout/base/nsCSSFrameConstructor.cpp | 22 +++++++++++----------- layout/base/nsChangeHint.h | 2 +- layout/base/nsPresShell.cpp | 8 ++++---- layout/mathml/nsMathMLTokenFrame.cpp | 2 +- layout/mathml/nsMathMLmtableFrame.cpp | 6 +++--- layout/style/nsCSSRuleProcessor.cpp | 6 +++--- layout/style/nsHTMLCSSStyleSheet.cpp | 2 +- layout/style/nsHTMLStyleSheet.cpp | 6 +++--- layout/style/nsStyleSet.cpp | 2 +- 11 files changed, 31 insertions(+), 31 deletions(-) diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp index eaa128013b27..92a6bac7d543 100644 --- a/layout/base/RestyleTracker.cpp +++ b/layout/base/RestyleTracker.cpp @@ -143,7 +143,7 @@ RestyleTracker::ProcessOneRestyle(Element* aElement, "Element has unexpected document"); nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); - if (aRestyleHint & eRestyle_Self) { + if (aRestyleHint & eRestyle_Subtree) { mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint, *this); } else if (aChangeHint && @@ -176,7 +176,7 @@ RestyleTracker::ProcessRestyles() sibling; sibling = sibling->GetNextSibling()) { if (sibling->IsElement() && - AddPendingRestyle(sibling->AsElement(), eRestyle_Self, + AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE)) { // Nothing else to do here; we'll handle the following // siblings when we get to |sibling| in laterSiblingArr. diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index 7e44c5185c5f..1f63116a141e 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -196,7 +196,7 @@ inline PRBool RestyleTracker::AddPendingRestyle(Element* aElement, // We can only treat this element as a restyle root if we would // actually restyle its descendants (so either call // ReResolveStyleContext on it or just reframe it). - if ((aRestyleHint & eRestyle_Self) || + if ((aRestyleHint & eRestyle_Subtree) || (aMinChangeHint & nsChangeHint_ReconstructFrame)) { for (const Element* cur = aElement; !cur->HasFlag(RootBit()); ) { nsIContent* parent = cur->GetFlattenedTreeParent(); diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index e8e226024499..0f2f64edb732 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -11358,7 +11358,7 @@ nsCSSFrameConstructor::RestyleForEmptyChange(Element* aContainer) // In some cases (:empty + E, :empty ~ E), a change if the content of // an element requires restyling its grandparent, because it changes // its parent's :empty state. - nsRestyleHint hint = eRestyle_Self; + nsRestyleHint hint = eRestyle_Subtree; nsIContent* grandparent = aContainer->GetParent(); if (grandparent && (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) { @@ -11408,7 +11408,7 @@ nsCSSFrameConstructor::RestyleForAppend(Element* aContainer, } if (selectorFlags & NODE_HAS_SLOW_SELECTOR) { - PostRestyleEvent(aContainer, eRestyle_Self, NS_STYLE_HINT_NONE); + PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE); // Restyling the container is the most we can do here, so we're done. return; } @@ -11419,7 +11419,7 @@ nsCSSFrameConstructor::RestyleForAppend(Element* aContainer, cur; cur = cur->GetPreviousSibling()) { if (cur->IsElement()) { - PostRestyleEvent(cur->AsElement(), eRestyle_Self, NS_STYLE_HINT_NONE); + PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE); break; } } @@ -11427,7 +11427,7 @@ nsCSSFrameConstructor::RestyleForAppend(Element* aContainer, } // Needed since we can't use PostRestyleEvent on non-elements (with -// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Self | +// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree | // eRestyle_LaterSiblings) as appropriate). static void RestyleSiblingsStartingWith(nsCSSFrameConstructor *aFrameConstructor, @@ -11438,7 +11438,7 @@ RestyleSiblingsStartingWith(nsCSSFrameConstructor *aFrameConstructor, if (sibling->IsElement()) { aFrameConstructor-> PostRestyleEvent(sibling->AsElement(), - nsRestyleHint(eRestyle_Self | eRestyle_LaterSiblings), + nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings), NS_STYLE_HINT_NONE); break; } @@ -11486,7 +11486,7 @@ nsCSSFrameConstructor::RestyleForInsertOrChange(Element* aContainer, } if (selectorFlags & NODE_HAS_SLOW_SELECTOR) { - PostRestyleEvent(aContainer, eRestyle_Self, NS_STYLE_HINT_NONE); + PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE); // Restyling the container is the most we can do here, so we're done. return; } @@ -11508,7 +11508,7 @@ nsCSSFrameConstructor::RestyleForInsertOrChange(Element* aContainer, } if (content->IsElement()) { if (passedChild) { - PostRestyleEvent(content->AsElement(), eRestyle_Self, + PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE); } break; @@ -11525,7 +11525,7 @@ nsCSSFrameConstructor::RestyleForInsertOrChange(Element* aContainer, } if (content->IsElement()) { if (passedChild) { - PostRestyleEvent(content->AsElement(), eRestyle_Self, + PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE); } break; @@ -11568,7 +11568,7 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer, } if (selectorFlags & NODE_HAS_SLOW_SELECTOR) { - PostRestyleEvent(aContainer, eRestyle_Self, NS_STYLE_HINT_NONE); + PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE); // Restyling the container is the most we can do here, so we're done. return; } @@ -11590,7 +11590,7 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer, } if (content->IsElement()) { if (reachedFollowingSibling) { - PostRestyleEvent(content->AsElement(), eRestyle_Self, + PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE); } break; @@ -11603,7 +11603,7 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer, content = content->GetPreviousSibling()) { if (content->IsElement()) { if (reachedFollowingSibling) { - PostRestyleEvent(content->AsElement(), eRestyle_Self, NS_STYLE_HINT_NONE); + PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE); } break; } diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 6677c7a61105..8ed6e687f459 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -144,7 +144,7 @@ inline PRBool NS_IsHintSubset(nsChangeHint aSubset, nsChangeHint aSuperSet) { * descendants." When no restyling is necessary, use |nsRestyleHint(0)|. */ enum nsRestyleHint { - eRestyle_Self = 0x1, + eRestyle_Subtree = 0x1, eRestyle_LaterSiblings = 0x2 }; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 3a35d9af9fc3..705221d7bfc5 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -3626,14 +3626,14 @@ PresShell::RecreateFramesFor(nsIContent* aContent) void nsIPresShell::PostRecreateFramesFor(Element* aElement) { - FrameConstructor()->PostRestyleEvent(aElement, eRestyle_Self, + FrameConstructor()->PostRestyleEvent(aElement, eRestyle_Subtree, nsChangeHint_ReconstructFrame); } void nsIPresShell::RestyleForAnimation(Element* aElement) { - FrameConstructor()->PostAnimationRestyleEvent(aElement, eRestyle_Self, + FrameConstructor()->PostAnimationRestyleEvent(aElement, eRestyle_Subtree, NS_STYLE_HINT_NONE); } @@ -4902,7 +4902,7 @@ PresShell::DocumentStatesChanged(nsIDocument* aDocument, mDocument->GetRootElement(), aStateMask)) { mFrameConstructor->PostRestyleEvent(mDocument->GetRootElement(), - eRestyle_Self, NS_STYLE_HINT_NONE); + eRestyle_Subtree, NS_STYLE_HINT_NONE); VERIFY_STYLE_TREE; } } @@ -5079,7 +5079,7 @@ nsIPresShell::ReconstructStyleDataInternal() return; } - mFrameConstructor->PostRestyleEvent(root, eRestyle_Self, NS_STYLE_HINT_NONE); + mFrameConstructor->PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE); #ifdef ACCESSIBILITY InvalidateAccessibleSubtree(nsnull); diff --git a/layout/mathml/nsMathMLTokenFrame.cpp b/layout/mathml/nsMathMLTokenFrame.cpp index df247535e7f2..86594bac96ff 100644 --- a/layout/mathml/nsMathMLTokenFrame.cpp +++ b/layout/mathml/nsMathMLTokenFrame.cpp @@ -276,7 +276,7 @@ nsMathMLTokenFrame::ProcessTextData() // explicitly request a re-resolve to pick up the change of style PresContext()->PresShell()->FrameConstructor()-> - PostRestyleEvent(mContent->AsElement(), eRestyle_Self, NS_STYLE_HINT_NONE); + PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE); } /////////////////////////////////////////////////////////////////////////// diff --git a/layout/mathml/nsMathMLmtableFrame.cpp b/layout/mathml/nsMathMLmtableFrame.cpp index d56adf7cc7f0..51053b876370 100644 --- a/layout/mathml/nsMathMLmtableFrame.cpp +++ b/layout/mathml/nsMathMLmtableFrame.cpp @@ -525,7 +525,7 @@ nsMathMLmtableOuterFrame::AttributeChanged(PRInt32 aNameSpaceID, // Explicitly request a re-resolve and reflow in our subtree to pick up any changes presContext->PresShell()->FrameConstructor()-> - PostRestyleEvent(mContent->AsElement(), eRestyle_Self, + PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, nsChangeHint_ReflowFrame); return NS_OK; @@ -705,7 +705,7 @@ nsMathMLmtableFrame::RestyleTable() // Explicitly request a re-resolve and reflow in our subtree to pick up any changes PresContext()->PresShell()->FrameConstructor()-> - PostRestyleEvent(mContent->AsElement(), eRestyle_Self, + PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, nsChangeHint_ReflowFrame); } @@ -766,7 +766,7 @@ nsMathMLmtrFrame::AttributeChanged(PRInt32 aNameSpaceID, // Explicitly request a re-resolve and reflow in our subtree to pick up any changes presContext->PresShell()->FrameConstructor()-> - PostRestyleEvent(mContent->AsElement(), eRestyle_Self, + PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, nsChangeHint_ReflowFrame); return NS_OK; diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index a092ee092ac2..5f301e05a53f 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -2356,7 +2356,7 @@ nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData) nsCSSSelector* selector = *iter; nsRestyleHint possibleChange = IsSiblingOperator(selector->mOperator) ? - eRestyle_LaterSiblings : eRestyle_Self; + eRestyle_LaterSiblings : eRestyle_Subtree; // If hint already includes all the bits of possibleChange, // don't bother calling SelectorMatches, since even if it returns false @@ -2398,7 +2398,7 @@ AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData) AttributeRuleProcessorData *data = aData->data; nsRestyleHint possibleChange = IsSiblingOperator(aSelector->mOperator) ? - eRestyle_LaterSiblings : eRestyle_Self; + eRestyle_LaterSiblings : eRestyle_Subtree; // If enumData->change already includes all the bits of possibleChange, don't // bother calling SelectorMatches, since even if it returns false @@ -2430,7 +2430,7 @@ nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData aData->mNameSpaceID == kNameSpaceID_XUL && aData->mElement == aData->mElement->GetOwnerDoc()->GetRootElement()) { - data.change = nsRestyleHint(data.change | eRestyle_Self); + data.change = nsRestyleHint(data.change | eRestyle_Subtree); } } diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index 9cba94fb9a05..e57134eef3be 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -156,7 +156,7 @@ nsHTMLCSSStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aDat // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but // it doesn't really matter. if (aData->mAttrHasChanged && aData->mAttribute == nsGkAtoms::style) { - return eRestyle_Self; + return eRestyle_Subtree; } return nsRestyleHint(0); diff --git a/layout/style/nsHTMLStyleSheet.cpp b/layout/style/nsHTMLStyleSheet.cpp index b55aa5d8ab3f..a5caba1d023a 100644 --- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -279,7 +279,7 @@ nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData) ((mActiveRule && (aData->mStateMask & NS_EVENT_STATE_ACTIVE)) || (mLinkRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)) || (mVisitedRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)))) { - return eRestyle_Self; + return eRestyle_Subtree; } return nsRestyleHint(0); @@ -309,7 +309,7 @@ nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) (mLinkRule || mVisitedRule || mActiveRule) && element->IsHTML() && aData->mContentTag == nsGkAtoms::a) { - return eRestyle_Self; + return eRestyle_Subtree; } // Don't worry about the mDocumentColorRule since it only applies @@ -317,7 +317,7 @@ nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) // Handle the content style rules. if (element->IsAttributeMapped(aData->mAttribute)) { - return eRestyle_Self; + return eRestyle_Subtree; } return nsRestyleHint(0); diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index d121efb5def2..32fa36d714d6 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1181,7 +1181,7 @@ static PRBool SheetHasDocumentStateStyle(nsIStyleRuleProcessor* aProcessor, { StatefulData* data = (StatefulData*)aData; if (aProcessor->HasDocumentStateDependentStyle(data)) { - data->mHint = eRestyle_Self; + data->mHint = eRestyle_Subtree; return PR_FALSE; // don't continue } return PR_TRUE; // continue From fa30dc04373d2febafa5a95a9f1b31f3fefdabda Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 11/17] Bug 494117 part 2. Don't force selector matching on the whole subtree rooted at an element when the element's style changes. r=dbaron --- layout/base/RestyleTracker.cpp | 5 ++- layout/base/RestyleTracker.h | 2 +- layout/base/nsCSSFrameConstructor.cpp | 10 ++--- layout/base/nsCSSFrameConstructor.h | 3 +- layout/base/nsChangeHint.h | 11 +++--- layout/base/nsFrameManager.cpp | 53 +++++++++++++++++++------ layout/base/nsFrameManager.h | 9 ++++- layout/base/nsPresShell.cpp | 9 ++++- layout/style/Makefile.in | 3 ++ layout/style/nsCSSRuleProcessor.cpp | 19 +++++---- layout/style/nsHTMLCSSStyleSheet.cpp | 2 +- layout/style/nsHTMLStyleSheet.cpp | 57 ++++++++++++--------------- layout/style/nsHTMLStyleSheet.h | 2 + layout/style/nsStyleSet.cpp | 2 +- 14 files changed, 117 insertions(+), 70 deletions(-) diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp index 92a6bac7d543..0b69e455042d 100644 --- a/layout/base/RestyleTracker.cpp +++ b/layout/base/RestyleTracker.cpp @@ -143,9 +143,10 @@ RestyleTracker::ProcessOneRestyle(Element* aElement, "Element has unexpected document"); nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); - if (aRestyleHint & eRestyle_Subtree) { + if (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) { mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint, - *this); + *this, + (aRestyleHint & eRestyle_Subtree) != 0); } else if (aChangeHint && (primaryFrame || (aChangeHint & nsChangeHint_ReconstructFrame))) { diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index 1f63116a141e..b8e69c098f68 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -196,7 +196,7 @@ inline PRBool RestyleTracker::AddPendingRestyle(Element* aElement, // We can only treat this element as a restyle root if we would // actually restyle its descendants (so either call // ReResolveStyleContext on it or just reframe it). - if ((aRestyleHint & eRestyle_Subtree) || + if ((aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) || (aMinChangeHint & nsChangeHint_ReconstructFrame)) { for (const Element* cur = aElement; !cur->HasFlag(RootBit()); ) { nsIContent* parent = cur->GetFlattenedTreeParent(); diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 0f2f64edb732..1648145cdd3b 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8102,7 +8102,8 @@ void nsCSSFrameConstructor::RestyleElement(Element *aElement, nsIFrame *aPrimaryFrame, nsChangeHint aMinHint, - RestyleTracker& aRestyleTracker) + RestyleTracker& aRestyleTracker, + PRBool aRestyleDescendants) { NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(), "frame/content mismatch"); @@ -8120,7 +8121,7 @@ nsCSSFrameConstructor::RestyleElement(Element *aElement, nsStyleChangeList changeList; mPresShell->FrameManager()-> ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint, - aRestyleTracker); + aRestyleTracker, aRestyleDescendants); ProcessRestyledFrames(changeList); } else { // no frames, reconstruct for content @@ -11356,8 +11357,7 @@ void nsCSSFrameConstructor::RestyleForEmptyChange(Element* aContainer) { // In some cases (:empty + E, :empty ~ E), a change if the content of - // an element requires restyling its grandparent, because it changes - // its parent's :empty state. + // an element requires restyling its parent's siblings. nsRestyleHint hint = eRestyle_Subtree; nsIContent* grandparent = aContainer->GetParent(); if (grandparent && @@ -11662,7 +11662,7 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint) // Note: The restyle tracker we pass in here doesn't matter. mPresShell->FrameManager()->ComputeStyleChangeFor(mPresShell->GetRootFrame(), &changeList, aExtraHint, - mPendingRestyles); + mPendingRestyles, PR_TRUE); // Process the required changes ProcessRestyledFrames(changeList); // Tell the style set it's safe to destroy the old rule tree. We diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 154f1b5ee746..b6e640f0da50 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -453,7 +453,8 @@ private: void RestyleElement(Element* aElement, nsIFrame* aPrimaryFrame, nsChangeHint aMinHint, - RestyleTracker& aRestyleTracker); + RestyleTracker& aRestyleTracker, + PRBool aRestyleDescendants); nsresult InitAndRestoreFrame (const nsFrameConstructorState& aState, nsIContent* aContent, diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 8ed6e687f459..511e08fe8770 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -139,13 +139,14 @@ inline PRBool NS_IsHintSubset(nsChangeHint aSubset, nsChangeHint aSuperSet) { nsChangeHint(NS_STYLE_HINT_REFLOW | nsChangeHint_ReconstructFrame) /** - * |nsRestyleHint| is a bitfield for the result of |HasStateDependentStyle| - * and |HasAttributeDependentStyle|. All values have an implied "and - * descendants." When no restyling is necessary, use |nsRestyleHint(0)|. + * |nsRestyleHint| is a bitfield for the result of + * |HasStateDependentStyle| and |HasAttributeDependentStyle|. When no + * restyling is necessary, use |nsRestyleHint(0)|. */ enum nsRestyleHint { - eRestyle_Subtree = 0x1, - eRestyle_LaterSiblings = 0x2 + eRestyle_Self = 0x1, + eRestyle_Subtree = 0x2, /* self and descendants */ + eRestyle_LaterSiblings = 0x4 /* implies "and descendants" */ }; diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 7007bb0edd7b..8387c5f8e9ba 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -962,6 +962,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, nsIContent *aParentContent, nsStyleChangeList *aChangeList, nsChangeHint aMinChange, + nsRestyleHint aRestyleHint, PRBool aFireAccessibilityEvents, RestyleTracker& aRestyleTracker) { @@ -1029,9 +1030,16 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, if (NS_UpdateHint(aMinChange, restyleData.mChangeHint)) { aChangeList->AppendChange(aFrame, content, restyleData.mChangeHint); } + aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint); } } - + + nsRestyleHint childRestyleHint = aRestyleHint; + + if (childRestyleHint == eRestyle_Self) { + childRestyleHint = nsRestyleHint(0); + } + nsStyleContext* parentContext; nsIFrame* resolvedChild = nsnull; // Get the frame providing the parent style context. If it is a @@ -1060,8 +1068,8 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // frame because it is visible or hidden withitn this frame. assumeDifferenceHint = ReResolveStyleContext(aPresContext, providerFrame, aParentContent, aChangeList, - aMinChange, PR_FALSE, - aRestyleTracker); + aMinChange, aRestyleHint, + PR_FALSE, aRestyleTracker); // The provider's new context becomes the parent context of // aFrame's context. @@ -1111,6 +1119,9 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // continuation). newContext = prevContinuationContext; } + else if (!aRestyleHint) { + newContext = styleSet->ReparentStyleContext(oldContext, parentContext); + } else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) { NS_ASSERTION(localContent, "non pseudo-element frame without content node"); @@ -1147,7 +1158,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, nsCSSPseudoElements::ePseudo_PseudoElementCount, "Unexpected pseudo type"); if (pseudoTag == nsCSSPseudoElements::firstLetter) { - NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame, + NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame, "firstLetter pseudoTag without a nsFirstLetterFrame"); nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame); pseudoContent = block->GetContent(); @@ -1162,6 +1173,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, "non pseudo-element frame without content node"); newContext = styleSet->ResolveStyleFor(content->AsElement(), parentContext); } + NS_ASSERTION(newContext, "failed to get new style context"); if (newContext) { if (!parentContext) { @@ -1196,7 +1208,9 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, newContext = oldContext; // new context failed, recover... } - // do additional contexts + // do additional contexts + // XXXbz might be able to avoid selector matching here in some + // cases; won't worry about it for now. PRInt32 contextIndex = -1; while (1 == 1) { nsStyleContext* oldExtraContext = nsnull; @@ -1265,16 +1279,22 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, NS_ASSERTION(!undisplayed->mStyle->GetPseudo(), "Shouldn't have random pseudo style contexts in the " "undisplayed map"); + nsRestyleHint thisChildHint = childRestyleHint; RestyleTracker::RestyleData undisplayedRestyleData; if (aRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(), &undisplayedRestyleData)) { - // Nothing to do with it for now; when we don't - // automatically restyle our kids we'll need to handle that - // here. We do want the GetRestyleData call, though, to - // preserve the restyle tracker's invariants. + thisChildHint = + nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint); + } + nsRefPtr undisplayedContext; + if (thisChildHint) { + undisplayedContext = + styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(), + newContext); + } else { + undisplayedContext = + styleSet->ReparentStyleContext(undisplayed->mStyle, newContext); } - nsRefPtr undisplayedContext = - styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(), newContext); if (undisplayedContext) { const nsStyleDisplay* display = undisplayedContext->GetStyleDisplay(); if (display->mDisplay != NS_STYLE_DISPLAY_NONE) { @@ -1410,6 +1430,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, content, aChangeList, NS_SubtractHint(aMinChange, nsChangeHint_ReflowFrame), + childRestyleHint, fireAccessibilityEvents, aRestyleTracker); @@ -1417,6 +1438,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // as the out-of-flow frame ReResolveStyleContext(aPresContext, child, content, aChangeList, aMinChange, + childRestyleHint, fireAccessibilityEvents, aRestyleTracker); } @@ -1424,6 +1446,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, if (child != resolvedChild) { ReResolveStyleContext(aPresContext, child, content, aChangeList, aMinChange, + childRestyleHint, fireAccessibilityEvents, aRestyleTracker); } else { @@ -1448,7 +1471,8 @@ void nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame, nsStyleChangeList *aChangeList, nsChangeHint aMinChange, - RestyleTracker& aRestyleTracker) + RestyleTracker& aRestyleTracker, + PRBool aRestyleDescendants) { if (aMinChange) { aChangeList->AppendChange(aFrame, aFrame->GetContent(), aMinChange); @@ -1473,7 +1497,10 @@ nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame, // Inner loop over next-in-flows of the current frame nsChangeHint frameChange = ReResolveStyleContext(GetPresContext(), frame, nsnull, - aChangeList, topLevelChange, PR_TRUE, + aChangeList, topLevelChange, + aRestyleDescendants ? + eRestyle_Subtree : eRestyle_Self, + PR_TRUE, aRestyleTracker); NS_UpdateHint(topLevelChange, frameChange); diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h index 3087a56efee1..29efc5d3df80 100644 --- a/layout/base/nsFrameManager.h +++ b/layout/base/nsFrameManager.h @@ -160,7 +160,8 @@ public: ComputeStyleChangeFor(nsIFrame* aFrame, nsStyleChangeList* aChangeList, nsChangeHint aMinChange, - RestyleTracker& aRestyleTracker); + RestyleTracker& aRestyleTracker, + PRBool aRestyleDescendants); /* * Capture/restore frame state for the frame subtree rooted at aFrame. @@ -201,12 +202,18 @@ public: } private: + // Use eRestyle_Self for the aRestyleHint argument to mean + // "reresolve our style context but not kids", use eRestyle_Subtree + // to mean "reresolve our style context and kids", and use + // nsRestyleHint(0) to mean recompute a new style context for our + // current parent and existing rulenode, and the same for kids. NS_HIDDEN_(nsChangeHint) ReResolveStyleContext(nsPresContext *aPresContext, nsIFrame *aFrame, nsIContent *aParentContent, nsStyleChangeList *aChangeList, nsChangeHint aMinChange, + nsRestyleHint aRestyleHint, PRBool aFireAccessibilityEvents, RestyleTracker& aRestyleTracker); }; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 705221d7bfc5..be938f4b873b 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -3626,14 +3626,19 @@ PresShell::RecreateFramesFor(nsIContent* aContent) void nsIPresShell::PostRecreateFramesFor(Element* aElement) { - FrameConstructor()->PostRestyleEvent(aElement, eRestyle_Subtree, + FrameConstructor()->PostRestyleEvent(aElement, nsRestyleHint(0), nsChangeHint_ReconstructFrame); } void nsIPresShell::RestyleForAnimation(Element* aElement) { - FrameConstructor()->PostAnimationRestyleEvent(aElement, eRestyle_Subtree, + // eRestyle_Self is ok here because animations are always tied to a + // particular element and don't directly affect its kids. The kids + // might have animations of their own, or inherit from aElement, but + // we handle all that during restyling; we don't need to _force_ + // animation rule matching on the kids here. + FrameConstructor()->PostAnimationRestyleEvent(aElement, eRestyle_Self, NS_STYLE_HINT_NONE); } diff --git a/layout/style/Makefile.in b/layout/style/Makefile.in index d72e449b5d83..8f3a4d941b01 100644 --- a/layout/style/Makefile.in +++ b/layout/style/Makefile.in @@ -147,6 +147,9 @@ FORCE_STATIC_LIB = 1 include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES = \ + -I$(srcdir)/../base \ + -I$(srcdir)/../generic \ + -I$(srcdir)/../xul/base/src \ -I$(srcdir)/../../content/base/src \ -I$(srcdir)/../../content/html/content/src \ -I$(srcdir)/../../content/xbl/src \ diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 5f301e05a53f..e24cc9eb26ea 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -2329,10 +2329,17 @@ nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData) } #endif -inline PRBool -IsSiblingOperator(PRUnichar oper) +static inline nsRestyleHint RestyleHintForOp(PRUnichar oper) { - return oper == PRUnichar('+') || oper == PRUnichar('~'); + if (oper == PRUnichar('+') || oper == PRUnichar('~')) { + return eRestyle_LaterSiblings; + } + + if (oper != PRUnichar(0)) { + return eRestyle_Subtree; + } + + return eRestyle_Self; } nsRestyleHint @@ -2355,8 +2362,7 @@ nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData) for(; iter != end; ++iter) { nsCSSSelector* selector = *iter; - nsRestyleHint possibleChange = IsSiblingOperator(selector->mOperator) ? - eRestyle_LaterSiblings : eRestyle_Subtree; + nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator); // If hint already includes all the bits of possibleChange, // don't bother calling SelectorMatches, since even if it returns false @@ -2397,8 +2403,7 @@ AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData) { AttributeRuleProcessorData *data = aData->data; - nsRestyleHint possibleChange = IsSiblingOperator(aSelector->mOperator) ? - eRestyle_LaterSiblings : eRestyle_Subtree; + nsRestyleHint possibleChange = RestyleHintForOp(aSelector->mOperator); // If enumData->change already includes all the bits of possibleChange, don't // bother calling SelectorMatches, since even if it returns false diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index e57134eef3be..9cba94fb9a05 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -156,7 +156,7 @@ nsHTMLCSSStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aDat // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but // it doesn't really matter. if (aData->mAttrHasChanged && aData->mAttribute == nsGkAtoms::style) { - return eRestyle_Subtree; + return eRestyle_Self; } return nsRestyleHint(0); diff --git a/layout/style/nsHTMLStyleSheet.cpp b/layout/style/nsHTMLStyleSheet.cpp index a5caba1d023a..c25c7d7665a9 100644 --- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -71,6 +71,7 @@ #include "nsContentErrors.h" #include "nsRuleProcessorData.h" #include "mozilla/dom/Element.h" +#include "nsCSSFrameConstructor.h" using namespace mozilla::dom; @@ -279,7 +280,7 @@ nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData) ((mActiveRule && (aData->mStateMask & NS_EVENT_STATE_ACTIVE)) || (mLinkRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)) || (mVisitedRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)))) { - return eRestyle_Subtree; + return eRestyle_Self; } return nsRestyleHint(0); @@ -309,7 +310,7 @@ nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) (mLinkRule || mVisitedRule || mActiveRule) && element->IsHTML() && aData->mContentTag == nsGkAtoms::a) { - return eRestyle_Subtree; + return eRestyle_Self; } // Don't worry about the mDocumentColorRule since it only applies @@ -317,7 +318,7 @@ nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) // Handle the content style rules. if (element->IsAttributeMapped(aData->mAttribute)) { - return eRestyle_Subtree; + return eRestyle_Self; } return nsRestyleHint(0); @@ -455,52 +456,46 @@ nsHTMLStyleSheet::Reset(nsIURI* aURL) } nsresult -nsHTMLStyleSheet::SetLinkColor(nscolor aColor) +nsHTMLStyleSheet::ImplLinkColorSetter(nsRefPtr& aRule, nscolor aColor) { - if (mLinkRule) { - if (mLinkRule->mColor == aColor) + if (aRule && aRule->mColor == aColor) { return NS_OK; } - mLinkRule = new HTMLColorRule(); - if (!mLinkRule) + aRule = new HTMLColorRule(); + if (!aRule) return NS_ERROR_OUT_OF_MEMORY; - mLinkRule->mColor = aColor; + aRule->mColor = aColor; + // Now make sure we restyle any links that might need it. This + // shouldn't happen often, so just rebuilding everything is ok. + if (mDocument && mDocument->GetPrimaryShell()) { + Element* root = mDocument->GetRootElement(); + if (root) { + mDocument->GetPrimaryShell()->FrameConstructor()-> + PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE); + } + } return NS_OK; } +nsresult +nsHTMLStyleSheet::SetLinkColor(nscolor aColor) +{ + return ImplLinkColorSetter(mLinkRule, aColor); +} + nsresult nsHTMLStyleSheet::SetActiveLinkColor(nscolor aColor) { - if (mActiveRule) { - if (mActiveRule->mColor == aColor) - return NS_OK; - } - - mActiveRule = new HTMLColorRule(); - if (!mActiveRule) - return NS_ERROR_OUT_OF_MEMORY; - - mActiveRule->mColor = aColor; - return NS_OK; + return ImplLinkColorSetter(mActiveRule, aColor); } nsresult nsHTMLStyleSheet::SetVisitedLinkColor(nscolor aColor) { - if (mVisitedRule) { - if (mVisitedRule->mColor == aColor) - return NS_OK; - } - - mVisitedRule = new HTMLColorRule(); - if (!mVisitedRule) - return NS_ERROR_OUT_OF_MEMORY; - - mVisitedRule->mColor = aColor; - return NS_OK; + return ImplLinkColorSetter(mVisitedRule, aColor); } already_AddRefed diff --git a/layout/style/nsHTMLStyleSheet.h b/layout/style/nsHTMLStyleSheet.h index 9a932f2f9f7c..a4311c2a5a9c 100644 --- a/layout/style/nsHTMLStyleSheet.h +++ b/layout/style/nsHTMLStyleSheet.h @@ -127,6 +127,8 @@ private: nscolor mColor; }; + // Implementation of SetLink/VisitedLink/ActiveLinkColor + nsresult ImplLinkColorSetter(nsRefPtr& aRule, nscolor aColor); class GenericTableRule; friend class GenericTableRule; diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 32fa36d714d6..d121efb5def2 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1181,7 +1181,7 @@ static PRBool SheetHasDocumentStateStyle(nsIStyleRuleProcessor* aProcessor, { StatefulData* data = (StatefulData*)aData; if (aProcessor->HasDocumentStateDependentStyle(data)) { - data->mHint = eRestyle_Subtree; + data->mHint = eRestyle_Self; return PR_FALSE; // don't continue } return PR_TRUE; // continue From 6b42fb408a7ab0d9051f3c1d2cc037868e7a40ec Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 12/17] Bug 569006. Skip probing for ::before/::after during reresolve if we're not redoing matching on kids, since those can't appear/disappear without either a reframe or a change in what selectors we match. r=dbaron --- layout/base/nsFrameManager.cpp | 13 ++++++++++--- layout/reftests/bugs/569006-1-ref.html | 6 ++++++ layout/reftests/bugs/569006-1.html | 10 ++++++++++ layout/reftests/bugs/reftest.list | 1 + layout/style/nsStyleStruct.cpp | 12 ++++++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 layout/reftests/bugs/569006-1-ref.html create mode 100644 layout/reftests/bugs/569006-1.html diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 8387c5f8e9ba..552db251f2e2 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -1312,7 +1312,11 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, } } - if (!(aMinChange & nsChangeHint_ReconstructFrame)) { + // Check whether we might need to create a new ::before frame. + // There's no need to do this if we're planning to reframe already + // or if we're not forcing restyles on kids. + if (!(aMinChange & nsChangeHint_ReconstructFrame) && + childRestyleHint) { // Make sure not to do this for pseudo-frames -- those can't have :before // or :after content. Neither can non-elements or leaf frames. if (!pseudoTag && localContent && localContent->IsElement() && @@ -1336,8 +1340,11 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, } } - - if (!(aMinChange & nsChangeHint_ReconstructFrame)) { + // Check whether we might need to create a new ::after frame. + // There's no need to do this if we're planning to reframe already + // or if we're not forcing restyles on kids. + if (!(aMinChange & nsChangeHint_ReconstructFrame) && + childRestyleHint) { // Make sure not to do this for pseudo-frames -- those can't have :before // or :after content. Neither can non-elements or leaf frames. if (!pseudoTag && localContent && localContent->IsElement() && diff --git a/layout/reftests/bugs/569006-1-ref.html b/layout/reftests/bugs/569006-1-ref.html new file mode 100644 index 000000000000..c62d2728dc7e --- /dev/null +++ b/layout/reftests/bugs/569006-1-ref.html @@ -0,0 +1,6 @@ + + + + This text should be visible + + diff --git a/layout/reftests/bugs/569006-1.html b/layout/reftests/bugs/569006-1.html new file mode 100644 index 000000000000..a6a9a280be62 --- /dev/null +++ b/layout/reftests/bugs/569006-1.html @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 3a5064646030..bcfe79cf7fc3 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1444,6 +1444,7 @@ random-if(!haveTestPlugin) == 546071-1.html 546071-1-ref.html == 564054-1.html 564054-1-ref.html == 565819-1.html 565819-ref.html == 565819-2.html 565819-ref.html +== 569006-1.html 569006-1-ref.html == 571281-1a.html 571281-1-ref.html == 571281-1b.html 571281-1-ref.html == 571281-1c.html 571281-1-ref.html diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 0528bf01bdcd..136402cb0304 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2124,6 +2124,18 @@ nsStyleContent::nsStyleContent(const nsStyleContent& aSource) nsChangeHint nsStyleContent::CalcDifference(const nsStyleContent& aOther) const { + // In ReResolveStyleContext we assume that if there's no existing + // ::before or ::after and we don't have to restyle children of the + // node then we can't end up with a ::before or ::after due to the + // restyle of the node itself. That's not quite true, but the only + // exception to the above is when the 'content' property of the node + // changes and the pseudo-element inherits the changed value. Since + // the code here triggers a frame change on the node in that case, + // the optimization in ReResolveStyleContext is ok. But if we ever + // change this code to not reconstruct frames on changes to the + // 'content' property, then we will need to revisit the optimization + // in ReResolveStyleContext. + if (mContentCount != aOther.mContentCount || mIncrementCount != aOther.mIncrementCount || mResetCount != aOther.mResetCount) { From c8d7e9da79979109a49b538a497e750b5c8e7b76 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 13/17] Bug 571347. In HasAttributeDependentStyle when the class attribute is changing, walk only selectors with classes that might be relevant. r=dbaron --- layout/reftests/bugs/571347-1-ref.html | 6 ++ layout/reftests/bugs/571347-1a.html | 10 +++ layout/reftests/bugs/571347-1b.html | 10 +++ layout/reftests/bugs/571347-2-ref.html | 6 ++ layout/reftests/bugs/571347-2a.html | 10 +++ layout/reftests/bugs/571347-2b.html | 10 +++ layout/reftests/bugs/571347-2c.html | 10 +++ layout/reftests/bugs/571347-2d.html | 10 +++ layout/reftests/bugs/reftest.list | 6 ++ layout/style/nsCSSRuleProcessor.cpp | 102 +++++++++++++++++++++++-- 10 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 layout/reftests/bugs/571347-1-ref.html create mode 100644 layout/reftests/bugs/571347-1a.html create mode 100644 layout/reftests/bugs/571347-1b.html create mode 100644 layout/reftests/bugs/571347-2-ref.html create mode 100644 layout/reftests/bugs/571347-2a.html create mode 100644 layout/reftests/bugs/571347-2b.html create mode 100644 layout/reftests/bugs/571347-2c.html create mode 100644 layout/reftests/bugs/571347-2d.html diff --git a/layout/reftests/bugs/571347-1-ref.html b/layout/reftests/bugs/571347-1-ref.html new file mode 100644 index 000000000000..08d20638cfce --- /dev/null +++ b/layout/reftests/bugs/571347-1-ref.html @@ -0,0 +1,6 @@ + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-1a.html b/layout/reftests/bugs/571347-1a.html new file mode 100644 index 000000000000..c2ca8ae09d00 --- /dev/null +++ b/layout/reftests/bugs/571347-1a.html @@ -0,0 +1,10 @@ + + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-1b.html b/layout/reftests/bugs/571347-1b.html new file mode 100644 index 000000000000..842e104fcf97 --- /dev/null +++ b/layout/reftests/bugs/571347-1b.html @@ -0,0 +1,10 @@ + + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-2-ref.html b/layout/reftests/bugs/571347-2-ref.html new file mode 100644 index 000000000000..66d0d452c8c5 --- /dev/null +++ b/layout/reftests/bugs/571347-2-ref.html @@ -0,0 +1,6 @@ + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-2a.html b/layout/reftests/bugs/571347-2a.html new file mode 100644 index 000000000000..fb9e23eeced9 --- /dev/null +++ b/layout/reftests/bugs/571347-2a.html @@ -0,0 +1,10 @@ + + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-2b.html b/layout/reftests/bugs/571347-2b.html new file mode 100644 index 000000000000..43610b1b4241 --- /dev/null +++ b/layout/reftests/bugs/571347-2b.html @@ -0,0 +1,10 @@ + + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-2c.html b/layout/reftests/bugs/571347-2c.html new file mode 100644 index 000000000000..fa8df1179d60 --- /dev/null +++ b/layout/reftests/bugs/571347-2c.html @@ -0,0 +1,10 @@ + + + + + This should be green + + diff --git a/layout/reftests/bugs/571347-2d.html b/layout/reftests/bugs/571347-2d.html new file mode 100644 index 000000000000..06d78f8dca05 --- /dev/null +++ b/layout/reftests/bugs/571347-2d.html @@ -0,0 +1,10 @@ + + + + + This should be green + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index bcfe79cf7fc3..3c5e20893919 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1448,3 +1448,9 @@ random-if(!haveTestPlugin) == 546071-1.html 546071-1-ref.html == 571281-1a.html 571281-1-ref.html == 571281-1b.html 571281-1-ref.html == 571281-1c.html 571281-1-ref.html +== 571347-1a.html 571347-1-ref.html +== 571347-1b.html 571347-1-ref.html +== 571347-2a.html 571347-2-ref.html +== 571347-2b.html 571347-2-ref.html +== 571347-2c.html 571347-2-ref.html +== 571347-2d.html 571347-2-ref.html diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index e24cc9eb26ea..82e547e31695 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -706,6 +706,67 @@ static const PLDHashTableOps AttributeSelectorOps = { }; +//-------------------------------- + +// Class selectors hash table. +struct ClassSelectorEntry : public PLDHashEntryHdr { + nsIAtom *mClass; + nsTArray mSelectors; +}; + +static void +ClassSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + (static_cast(hdr))->~ClassSelectorEntry(); +} + +static PRBool +ClassSelector_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr, + const void *key) +{ + ClassSelectorEntry *entry = static_cast(hdr); + new (entry) ClassSelectorEntry(); + entry->mClass = const_cast(static_cast(key)); + return PR_TRUE; +} + +static nsIAtom* +ClassSelector_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr) +{ + const ClassSelectorEntry *entry = static_cast(hdr); + return entry->mClass; +} + +// Case-sensitive ops. +static const RuleHashTableOps ClassSelector_CSOps = { + { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashVoidPtrKeyStub, + RuleHash_CSMatchEntry, + PL_DHashMoveEntryStub, + ClassSelector_ClearEntry, + PL_DHashFinalizeStub, + ClassSelector_InitEntry + }, + ClassSelector_GetKey +}; + +// Case-insensitive ops. +static const RuleHashTableOps ClassSelector_CIOps = { + { + PL_DHashAllocTable, + PL_DHashFreeTable, + RuleHash_CIHashKey, + RuleHash_CIMatchEntry, + PL_DHashMoveEntryStub, + ClassSelector_ClearEntry, + PL_DHashFinalizeStub, + ClassSelector_InitEntry + }, + ClassSelector_GetKey +}; + //-------------------------------- struct RuleCascadeData { @@ -721,6 +782,10 @@ struct RuleCascadeData { sizeof(AttributeSelectorEntry), 16); PL_DHashTableInit(&mAnonBoxRules, &RuleHash_TagTable_Ops, nsnull, sizeof(RuleHashTagTableEntry), 16); + PL_DHashTableInit(&mClassSelectors, + aQuirksMode ? &ClassSelector_CIOps.ops : + &ClassSelector_CSOps.ops, + nsnull, sizeof(ClassSelectorEntry), 16); memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes)); #ifdef MOZ_XUL PL_DHashTableInit(&mXULTreeRules, &RuleHash_TagTable_Ops, nsnull, @@ -732,7 +797,10 @@ struct RuleCascadeData { { PL_DHashTableFinish(&mAttributeSelectors); PL_DHashTableFinish(&mAnonBoxRules); + PL_DHashTableFinish(&mClassSelectors); +#ifdef MOZ_XUL PL_DHashTableFinish(&mXULTreeRules); +#endif for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mPseudoElementRuleHashes); ++i) { delete mPseudoElementRuleHashes[i]; } @@ -742,7 +810,7 @@ struct RuleCascadeData { mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount]; nsTArray mStateSelectors; PRUint32 mSelectorDocumentStates; - nsTArray mClassSelectors; + PLDHashTable mClassSelectors; nsTArray mIDSelectors; PLDHashTable mAttributeSelectors; PLDHashTable mAnonBoxRules; @@ -2457,10 +2525,23 @@ nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData } if (aData->mAttribute == aData->mElement->GetClassAttributeName()) { - nsCSSSelector **iter = cascade->mClassSelectors.Elements(), - **end = iter + cascade->mClassSelectors.Length(); - for(; iter != end; ++iter) { - AttributeEnumFunc(*iter, &data); + const nsAttrValue* elementClasses = aData->mClasses; + if (elementClasses) { + PRInt32 atomCount = elementClasses->GetAtomCount(); + for (PRInt32 i = 0; i < atomCount; ++i) { + nsIAtom* curClass = elementClasses->AtomAt(i); + ClassSelectorEntry *entry = + static_cast + (PL_DHashTableOperate(&cascade->mClassSelectors, + curClass, PL_DHASH_LOOKUP)); + if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + nsCSSSelector **iter = entry->mSelectors.Elements(), + **end = iter + entry->mSelectors.Length(); + for(; iter != end; ++iter) { + AttributeEnumFunc(*iter, &data); + } + } + } } } @@ -2585,8 +2666,15 @@ AddSelector(RuleCascadeData* aCascade, } // Build mClassSelectors - if (aSelectorPart->mClassList) { - aCascade->mClassSelectors.AppendElement(aSelectorInTopLevel); + for (nsAtomList* curClass = aSelectorPart->mClassList; curClass; + curClass = curClass->mNext) { + ClassSelectorEntry *entry = + static_cast(PL_DHashTableOperate(&aCascade->mClassSelectors, + curClass->mAtom, + PL_DHASH_ADD)); + if (entry) { + entry->mSelectors.AppendElement(aSelectorInTopLevel); + } } // Build mAttributeSelectors. From ab557498b0164f92dc6a4f9a7ffbb356cfc5f305 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 14/17] Bug 571298. Avoid flash of unstyled content in document.write-created documents. r=jst --- content/html/document/src/nsHTMLDocument.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index eed08fe0f227..e6c82c8f2c55 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -1939,6 +1939,15 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) } } + // Flag us as not being able to start layout until we hit + // or scripts that require layout, so that we won't run into FOUC + // issues. We need to do that before making the Stop() call, + // since if we have no frame yet the flush Stop() triggers might + // try to create one for us, and we don't want our presshell + // starting layout if that happens. But we don't want to do this + // before the PermitUnload call above. + mMayStartLayout = PR_FALSE; + nsCOMPtr webnav(do_QueryInterface(shell)); webnav->Stop(nsIWebNavigation::STOP_NETWORK); @@ -1947,6 +1956,10 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) // document again otherwise the document could have a non-zero onload block // count without the onload blocker request being in the loadgroup. EnsureOnloadBlocker(); + } else { + // See comment before the mMayStartLayout set in the other branch + // of this if. + mMayStartLayout = PR_FALSE; } // The open occurred after the document finished loading. From 0f77839ff155c7c89b89b0d5159a6eabb2d06887 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 18 Jun 2010 12:23:05 -0400 Subject: [PATCH 15/17] Bug 517056. Drawing an incomplete image to the canvas shouldn't throw; should instead silently do nothing. r=vlad --- .../canvas/src/nsCanvasRenderingContext2D.cpp | 6 +- content/canvas/test/Makefile.in | 1 + .../canvas/test/test_drawImageIncomplete.html | 60 +++++++++++++++++++ layout/base/nsLayoutUtils.cpp | 15 ++++- layout/base/nsLayoutUtils.h | 5 ++ 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 content/canvas/test/test_drawImageIncomplete.html diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index a2b6b0c9ae0c..d7fb50b1e489 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -3105,8 +3105,10 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1, PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME; nsLayoutUtils::SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags); - if (!res.mSurface) - return NS_ERROR_NOT_AVAILABLE; + if (!res.mSurface) { + // Spec says to silently do nothing if the element is still loading. + return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE; + } #ifndef WINCE // On non-CE, force a copy if we're using drawImage with our destination diff --git a/content/canvas/test/Makefile.in b/content/canvas/test/Makefile.in index 79046c837256..0e8df6190f82 100644 --- a/content/canvas/test/Makefile.in +++ b/content/canvas/test/Makefile.in @@ -65,6 +65,7 @@ _TEST_FILES_0 = \ image_green.png \ image_green-redirect \ image_green-redirect^headers^ \ + test_drawImageIncomplete.html \ $(NULL) # xor and lighter aren't well handled by cairo; they mostly work, but we don't want diff --git a/content/canvas/test/test_drawImageIncomplete.html b/content/canvas/test/test_drawImageIncomplete.html new file mode 100644 index 000000000000..b90884d90f3a --- /dev/null +++ b/content/canvas/test/test_drawImageIncomplete.html @@ -0,0 +1,60 @@ + + + + + Test for Bug 517056 + + + + + +Mozilla Bug 517056 +

    + +

    + +
    +
    +
    + + diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 10856c2400ad..5ec5d5520676 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3441,6 +3441,14 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement, if (node && ve) { nsHTMLVideoElement *video = static_cast(ve.get()); + unsigned short readyState; + if (NS_SUCCEEDED(ve->GetReadyState(&readyState)) && + (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING || + readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) { + result.mIsStillLoading = PR_TRUE; + return result; + } + // If it doesn't have a principal, just bail nsCOMPtr principal = video->GetCurrentPrincipal(); if (!principal) @@ -3492,8 +3500,13 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement, PRUint32 status; imgRequest->GetImageStatus(&status); - if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) + if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) { + // Spec says to use GetComplete, but that only works on + // nsIDOMHTMLImageElement, and we support all sorts of other stuff + // here. Do this for now pending spec clarification. + result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0; return result; + } // In case of data: URIs, we want to ignore principals; // they should have the originating content's principal, diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index bc08936a4406..97c65d157c02 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1144,6 +1144,8 @@ public: }; struct SurfaceFromElementResult { + SurfaceFromElementResult() : mIsStillLoading(PR_FALSE) {} + /* mSurface will contain the resulting surface, or will be NULL on error */ nsRefPtr mSurface; /* The size of the surface */ @@ -1152,6 +1154,9 @@ public: nsCOMPtr mPrincipal; /* Whether the element was "write only", that is, the bits should not be exposed to content */ PRBool mIsWriteOnly; + /* Whether the element was still loading. Some consumers need to handle + this case specially. */ + PRBool mIsStillLoading; }; static SurfaceFromElementResult SurfaceFromElement(nsIDOMElement *aElement, From 819ab2079ddbd1ee852a65e289fd3e82287b414c Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 14 Jun 2010 16:06:49 -0400 Subject: [PATCH 16/17] Bug 563327 part 1. Expose an nsIPresShell API for refresh observers. r=roc --- layout/base/nsCSSFrameConstructor.cpp | 6 ++--- layout/base/nsIPresShell.h | 32 +++++++++++++++++++++++++++ layout/base/nsPresShell.cpp | 30 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 1648145cdd3b..f37d1a959a72 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -11748,8 +11748,7 @@ nsCSSFrameConstructor::PostRestyleEventInternal(PRBool aForLazyConstruction) // add ourselves as a refresh observer until then. PRBool inRefresh = aForLazyConstruction ? mInLazyFCRefresh : mInStyleRefresh; if (!mObservingRefreshDriver && !inRefresh) { - mObservingRefreshDriver = mPresShell->GetPresContext()-> - RefreshDriver()->AddRefreshObserver(this, Flush_Style); + mObservingRefreshDriver = mPresShell->AddRefreshObserver(this, Flush_Style); } } @@ -11761,8 +11760,7 @@ nsCSSFrameConstructor::WillRefresh(mozilla::TimeStamp aTime) // a refresh so we don't restart due to animation-triggered // restyles. The actual work of processing our restyles will get // done when the refresh driver flushes styles. - mPresShell->GetPresContext()->RefreshDriver()-> - RemoveRefreshObserver(this, Flush_Style); + mPresShell->RemoveRefreshObserver(this, Flush_Style); mObservingRefreshDriver = PR_FALSE; mInLazyFCRefresh = PR_TRUE; mInStyleRefresh = PR_TRUE; diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index f0fce480c391..e710ee826dae 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -67,6 +67,7 @@ #include "mozFlushType.h" #include "nsWeakReference.h" #include // for FILE definition +#include "nsRefreshDriver.h" class nsIContent; class nsIDocument; @@ -999,6 +1000,37 @@ public: PRUint64 GetPaintCount() { return mPaintCount; } void IncrementPaintCount() { ++mPaintCount; } + /** + * Refresh observer management. + */ +protected: + virtual PRBool AddRefreshObserverExternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType); + PRBool AddRefreshObserverInternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType); + virtual PRBool RemoveRefreshObserverExternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType); + PRBool RemoveRefreshObserverInternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType); +public: + PRBool AddRefreshObserver(nsARefreshObserver* aObserver, + mozFlushType aFlushType) { +#ifdef _IMPL_NS_LAYOUT + return AddRefreshObserverInternal(aObserver, aFlushType); +#else + return AddRefreshObserverExternal(aObserver, aFlushType); +#endif + } + + PRBool RemoveRefreshObserver(nsARefreshObserver* aObserver, + mozFlushType aFlushType) { +#ifdef _IMPL_NS_LAYOUT + return RemoveRefreshObserverInternal(aObserver, aFlushType); +#else + return RemoveRefreshObserverExternal(aObserver, aFlushType); +#endif + } + /** * Initialize and shut down static variables. */ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index be938f4b873b..ece5a3a8c13a 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -7749,6 +7749,36 @@ PresShell::Observe(nsISupports* aSubject, return NS_ERROR_FAILURE; } +PRBool +nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType) +{ + return GetPresContext()->RefreshDriver()-> + AddRefreshObserver(aObserver, aFlushType); +} + +/* virtual */ PRBool +nsIPresShell::AddRefreshObserverExternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType) +{ + return AddRefreshObserverInternal(aObserver, aFlushType); +} + +PRBool +nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType) +{ + return GetPresContext()->RefreshDriver()-> + RemoveRefreshObserver(aObserver, aFlushType); +} + +/* virtual */ PRBool +nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver, + mozFlushType aFlushType) +{ + return RemoveRefreshObserverInternal(aObserver, aFlushType); +} + //------------------------------------------------------ // End of protected and private methods on the PresShell //------------------------------------------------------ From 4658f4444e37a82be3f9374b44418d25e5527d36 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 14 Jun 2010 16:06:49 -0400 Subject: [PATCH 17/17] Bug 563327 part 2. Drive accessibility events off the refresh driver instead of an ad-hoc timer. r=surkov --- accessible/src/base/nsEventShell.cpp | 65 ++++++++++++++-------------- accessible/src/base/nsEventShell.h | 23 +++++----- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/accessible/src/base/nsEventShell.cpp b/accessible/src/base/nsEventShell.cpp index 97b8804173bd..5fa020035ff6 100644 --- a/accessible/src/base/nsEventShell.cpp +++ b/accessible/src/base/nsEventShell.cpp @@ -102,7 +102,7 @@ nsCOMPtr nsEventShell::sEventTargetNode; //////////////////////////////////////////////////////////////////////////////// nsAccEventQueue::nsAccEventQueue(nsDocAccessible *aDocument): - mProcessingStarted(PR_FALSE), mDocument(aDocument), mFlushingEventsCount(0) + mObservingRefresh(PR_FALSE), mDocument(aDocument) { } @@ -157,6 +157,13 @@ nsAccEventQueue::Push(nsAccEvent *aEvent) void nsAccEventQueue::Shutdown() { + if (mObservingRefresh) { + nsCOMPtr shell = mDocument->GetPresShell(); + if (!shell || + shell->RemoveRefreshObserver(this, Flush_Display)) { + mObservingRefresh = PR_FALSE; + } + } mDocument = nsnull; mEvents.Clear(); } @@ -169,14 +176,19 @@ nsAccEventQueue::PrepareFlush() { // If there are pending events in the queue and events flush isn't planed // yet start events flush asynchronously. - if (mEvents.Length() > 0 && !mProcessingStarted) { - NS_DISPATCH_RUNNABLEMETHOD(Flush, this) - mProcessingStarted = PR_TRUE; + if (mEvents.Length() > 0 && !mObservingRefresh) { + nsCOMPtr shell = mDocument->GetPresShell(); + // Use a Flush_Display observer so that it will get called after + // style and ayout have been flushed. + if (shell && + shell->AddRefreshObserver(this, Flush_Display)) { + mObservingRefresh = PR_TRUE; + } } } void -nsAccEventQueue::Flush() +nsAccEventQueue::WillRefresh(mozilla::TimeStamp aTime) { // If the document accessible is now shut down, don't fire events in it // anymore. @@ -187,42 +199,31 @@ nsAccEventQueue::Flush() if (!presShell) return; - // Flush layout so that all the frame construction, reflow, and styles are - // up-to-date. This will ensure we can get frames for the related nodes, as - // well as get the most current information for calculating things like - // visibility. We don't flush the display because we don't care about - // painting. If no flush is necessary the method will simple return. - presShell->FlushPendingNotifications(Flush_Layout); - // Process only currently queued events. Newly appended events during events // flushing won't be processed. - mFlushingEventsCount = mEvents.Length(); - NS_ASSERTION(mFlushingEventsCount, - "How did we get here without events to fire?"); + nsTArray < nsRefPtr > events; + events.SwapElements(mEvents); + PRUint32 length = events.Length(); + NS_ASSERTION(length, "How did we get here without events to fire?"); - for (PRUint32 index = 0; index < mFlushingEventsCount; index ++) { + for (PRUint32 index = 0; index < length; index ++) { // No presshell means the document was shut down during event handling // by AT. if (!mDocument || !mDocument->HasWeakShell()) break; - nsAccEvent *accEvent = mEvents[index]; + nsAccEvent *accEvent = events[index]; if (accEvent->mEventRule != nsAccEvent::eDoNotEmit) mDocument->ProcessPendingEvent(accEvent); } - // Mark we are ready to start event processing again. - mProcessingStarted = PR_FALSE; - - // If the document accessible is alive then remove processed events from the - // queue (otherwise they were removed on shutdown already) and reinitialize - // queue processing callback if necessary (new events might occur duiring - // delayed event processing). - if (mDocument && mDocument->HasWeakShell()) { - mEvents.RemoveElementsAt(0, mFlushingEventsCount); - mFlushingEventsCount = 0; - PrepareFlush(); + if (mEvents.Length() == 0) { + nsCOMPtr shell = mDocument->GetPresShell(); + if (!shell || + shell->RemoveRefreshObserver(this, Flush_Display)) { + mObservingRefresh = PR_FALSE; + } } } @@ -241,7 +242,7 @@ nsAccEventQueue::CoalesceEvents() switch(tailEvent->mEventRule) { case nsAccEvent::eCoalesceFromSameSubtree: { - for (PRInt32 index = tail - 1; index >= mFlushingEventsCount; index--) { + for (PRInt32 index = tail - 1; index >= 0; index--) { nsAccEvent* thisEvent = mEvents[index]; if (thisEvent->mEventType != tailEvent->mEventType) @@ -365,7 +366,7 @@ nsAccEventQueue::CoalesceEvents() // Do not emit thisEvent, also apply this result to sibling nodes of // thisNode. thisEvent->mEventRule = nsAccEvent::eDoNotEmit; - ApplyToSiblings(mFlushingEventsCount, index, thisEvent->mEventType, + ApplyToSiblings(0, index, thisEvent->mEventType, thisEvent->mNode, nsAccEvent::eDoNotEmit); continue; } @@ -386,7 +387,7 @@ nsAccEventQueue::CoalesceEvents() // Used for focus event, coalesce more older event since focus event // for accessible can be duplicated by event for its document, we are // interested in focus event for accessible. - for (PRInt32 index = tail - 1; index >= mFlushingEventsCount; index--) { + for (PRInt32 index = tail - 1; index >= 0; index--) { nsAccEvent* thisEvent = mEvents[index]; if (thisEvent->mEventType == tailEvent->mEventType && thisEvent->mEventRule == tailEvent->mEventRule && @@ -401,7 +402,7 @@ nsAccEventQueue::CoalesceEvents() { // Check for repeat events, coalesce newly appended event by more older // event. - for (PRInt32 index = tail - 1; index >= mFlushingEventsCount; index--) { + for (PRInt32 index = tail - 1; index >= 0; index--) { nsAccEvent* accEvent = mEvents[index]; if (accEvent->mEventType == tailEvent->mEventType && accEvent->mEventRule == tailEvent->mEventRule && diff --git a/accessible/src/base/nsEventShell.h b/accessible/src/base/nsEventShell.h index 7dbd74002b76..bab821ed621d 100644 --- a/accessible/src/base/nsEventShell.h +++ b/accessible/src/base/nsEventShell.h @@ -45,6 +45,8 @@ #include "nsAutoPtr.h" +#include "nsRefreshDriver.h" + class nsIPersistentProperties; /** @@ -90,7 +92,8 @@ private: /** * Event queue. */ -class nsAccEventQueue : public nsISupports +class nsAccEventQueue : public nsISupports, + public nsARefreshObserver { public: nsAccEventQueue(nsDocAccessible *aDocument); @@ -120,9 +123,7 @@ private: * Process pending events. It calls nsDocAccessible::ProcessPendingEvent() * where the real event processing is happen. */ - void Flush(); - - NS_DECL_RUNNABLEMETHOD(nsAccEventQueue, Flush) + virtual void WillRefresh(mozilla::TimeStamp aTime); /** * Coalesce redundant events from the queue. @@ -157,9 +158,10 @@ private: nsAccEvent *aDescendantAccEvent); /** - * Indicates whether events flush is run. + * Indicates whether we're waiting on a refresh notification from our + * presshell to flush events */ - PRBool mProcessingStarted; + PRBool mObservingRefresh; /** * The document accessible reference owning this queue. @@ -167,13 +169,8 @@ private: nsRefPtr mDocument; /** - * The number of events processed currently. Used to avoid event coalescence - * with new events appended to the queue because of events handling. - */ - PRInt32 mFlushingEventsCount; - - /** - * Pending events array. + * Pending events array. Don't make this an nsAutoTArray; we use + * SwapElements() on it. */ nsTArray > mEvents; };