This commit is contained in:
Robert Sayre 2010-06-18 09:34:01 -07:00
commit bc461689df
43 changed files with 1250 additions and 312 deletions

View File

@ -102,7 +102,7 @@ nsCOMPtr<nsINode> 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<nsIPresShell> 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<nsIPresShell> 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<nsAccEvent> > 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<nsIPresShell> 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 &&

View File

@ -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<nsDocAccessible> 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<nsRefPtr<nsAccEvent> > mEvents;
};

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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
@ -3481,14 +3483,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;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,60 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=517056
-->
<head>
<title>Test for Bug 517056</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=517056">Mozilla Bug 517056</a>
<p id="display">
<canvas id="c" width="1" height="1"></canvas>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 517056 **/
var ctx = $("c").getContext('2d');
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 1, 1);
var data = ctx.getImageData(0, 0, 1, 1).data;
is(data[0], 0, "Red channel of black should be 0");
is(data[1], 0, "Green channel of black should be 0");
is(data[2], 0, "Blue channel of black should be 0")
is(data[3], 255, "Alpha channel of black should be opaque");
var img = new Image();
// Force a new URI every time, so that we don't run into stupid caching issues.
img.src = "image_green-1x1.png?" + (new Date + 0) + Math.random();
// This shouldn't throw
ctx.drawImage(img, 0, 0);
var data = ctx.getImageData(0, 0, 1, 1).data;
is(data[0], 0, "Red channel of black should be 0 and image should have been ignored");
is(data[1], 0, "Green channel of black should be 0 and image should have been ignored");
is(data[2], 0, "Blue channel of black should be 0 and image should have been ignored")
is(data[3], 255, "Alpha channel of black should be opaque and image should have been ignored");
SimpleTest.waitForExplicitFinish();
img.onload = function() {
ctx.drawImage(img, 0, 0);
var data = ctx.getImageData(0, 0, 1, 1).data;
is(data[0], 0, "Red channel of green should be 0");
is(data[1], 255, "Green channel of green should be 255");
is(data[2], 0, "Blue channel of green should be 0")
is(data[3], 255, "Alpha channel of green should be opaque");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -886,17 +886,20 @@ 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(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32);
//----------------------------------------------------------------------
/**

View File

@ -1939,6 +1939,15 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
}
}
// Flag us as not being able to start layout until we hit <body>
// 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<nsIWebNavigation> 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.

View File

@ -449,7 +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(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET < 32);
class nsScriptEventHandlerOwnerTearoff;

View File

@ -91,6 +91,7 @@ EXPORTS = \
CPPSRCS = \
FrameLayerBuilder.cpp \
FramePropertyTable.cpp \
RestyleTracker.cpp \
nsCSSColorUtils.cpp \
nsCSSFrameConstructor.cpp \
nsCSSRendering.cpp \

View File

@ -0,0 +1,315 @@
/* -*- 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 <bzbarsky@mit.edu> (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"
#include "nsStyleChangeList.h"
namespace mozilla {
namespace css {
inline nsIDocument*
RestyleTracker::Document() const {
return mFrameConstructor->mDocument;
}
#define RESTYLE_ARRAY_STACKSIZE 128
struct LaterSiblingCollector {
RestyleTracker* tracker;
nsTArray< nsRefPtr<dom::Element> >* elements;
};
static PLDHashOperator
CollectLaterSiblings(nsISupports* aElement,
RestyleTracker::RestyleData& aData,
void* aSiblingCollector)
{
dom::Element* element =
static_cast<dom::Element*>(aElement);
LaterSiblingCollector* collector =
static_cast<LaterSiblingCollector*>(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* aRestyleCollector)
{
dom::Element* element =
static_cast<dom::Element*>(aElement);
RestyleCollector* collector =
static_cast<RestyleCollector*>(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 =
collector->restyleArrayPtr;
RestyleTracker::RestyleEnumerateData* currentRestyle =
*restyleArrayPtr;
currentRestyle->mElement = element;
currentRestyle->mRestyleHint = aData.mRestyleHint;
currentRestyle->mChangeHint = aData.mChangeHint;
// Increment to the next slot in the array
*restyleArrayPtr = currentRestyle + 1;
return PL_DHASH_NEXT;
}
inline void
RestyleTracker::ProcessOneRestyle(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint)
{
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 | eRestyle_Subtree)) {
mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint,
*this,
(aRestyleHint & eRestyle_Subtree) != 0);
} 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);
}
}
void
RestyleTracker::ProcessRestyles()
{
// 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 (mPendingRestyles.Count()) {
if (mHaveLaterSiblingRestyles) {
// Convert them to individual restyles on all the later siblings
nsAutoTArray<nsRefPtr<Element>, 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_Subtree,
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;
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<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
RestyleEnumerateData* restylesToProcess =
restyleArr.AppendElements(mPendingRestyles.Count());
if (restylesToProcess) {
RestyleEnumerateData* lastRestyle = restylesToProcess;
RestyleCollector collector = { this, &lastRestyle };
mPendingRestyles.Enumerate(CollectRestyles, &collector);
// 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);
}
}
}
// 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
}
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

View File

@ -0,0 +1,237 @@
/* -*- 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 <bzbarsky@mit.edu> (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"
#include "nsIFrame.h"
class nsCSSFrameConstructor;
namespace mozilla {
namespace css {
class RestyleTracker {
public:
typedef mozilla::dom::Element Element;
RestyleTracker(PRUint32 aRestyleBits,
nsCSSFrameConstructor* aFrameConstructor) :
mRestyleBits(aRestyleBits), mFrameConstructor(aFrameConstructor),
mHaveLaterSiblingRestyles(PR_FALSE)
{
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. Returns true
* if the element already had eRestyle_LaterSiblings set on it.
*/
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<Element> mElement;
};
private:
/**
* Handle a single mPendingRestyles entry. aRestyleHint must not
* include eRestyle_LaterSiblings; that needs to be dealt with
* before calling this function.
*/
inline void ProcessOneRestyle(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint);
typedef nsDataHashtable<nsISupportsHashKey, RestyleData> PendingRestyleTable;
typedef nsAutoTArray< nsRefPtr<Element>, 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 PRBool RestyleTracker::AddPendingRestyle(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
RestyleData existingData;
existingData.mRestyleHint = nsRestyleHint(0);
existingData.mChangeHint = NS_STYLE_HINT_NONE;
// 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);
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 | eRestyle_Subtree)) ||
(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
} // namespacs mozilla
#endif /* mozilla_css_RestyleTracker_h */

View File

@ -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()) {
@ -2333,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<nsStyleContext> styleContext;
styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
@ -5024,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)) {
@ -8079,7 +8101,9 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
void
nsCSSFrameConstructor::RestyleElement(Element *aElement,
nsIFrame *aPrimaryFrame,
nsChangeHint aMinHint)
nsChangeHint aMinHint,
RestyleTracker& aRestyleTracker,
PRBool aRestyleDescendants)
{
NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
"frame/content mismatch");
@ -8096,7 +8120,8 @@ nsCSSFrameConstructor::RestyleElement(Element *aElement,
} else if (aPrimaryFrame) {
nsStyleChangeList changeList;
mPresShell->FrameManager()->
ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint);
ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
aRestyleTracker, aRestyleDescendants);
ProcessRestyledFrames(changeList);
} else {
// no frames, reconstruct for content
@ -8104,20 +8129,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,
@ -9633,7 +9644,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());
@ -10957,6 +10974,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<nsStyleContext> childContext =
ResolveStyleContext(parentStyleContext, content);
@ -11333,9 +11357,8 @@ 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.
nsRestyleHint hint = eRestyle_Self;
// an element requires restyling its parent's siblings.
nsRestyleHint hint = eRestyle_Subtree;
nsIContent* grandparent = aContainer->GetParent();
if (grandparent &&
(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
@ -11385,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;
}
@ -11396,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;
}
}
@ -11404,17 +11427,20 @@ 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,
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_Subtree | eRestyle_LaterSiblings),
NS_STYLE_HINT_NONE);
break;
}
}
}
@ -11460,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;
}
@ -11482,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;
@ -11499,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;
@ -11542,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;
}
@ -11564,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;
@ -11577,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;
}
@ -11589,59 +11615,6 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer,
}
static PLDHashOperator
CollectRestyles(nsISupports* aElement,
nsCSSFrameConstructor::RestyleData& aData,
void* aRestyleArrayPtr)
{
nsCSSFrameConstructor::RestyleEnumerateData** restyleArrayPtr =
static_cast<nsCSSFrameConstructor::RestyleEnumerateData**>
(aRestyleArrayPtr);
nsCSSFrameConstructor::RestyleEnumerateData* currentRestyle =
*restyleArrayPtr;
currentRestyle->mElement = static_cast<Element*>(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,
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);
}
}
#define RESTYLE_ARRAY_STACKSIZE 128
void
nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint)
{
@ -11686,8 +11659,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, PR_TRUE);
// Process the required changes
ProcessRestyledFrames(changeList);
// Tell the style set it's safe to destroy the old rule tree. We
@ -11699,58 +11674,6 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint)
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
}
void
nsCSSFrameConstructor::ProcessPendingRestyleTable(
nsDataHashtable<nsISupportsHashKey, RestyleData>& 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<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> 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 +11687,7 @@ nsCSSFrameConstructor::ProcessPendingRestyles()
"Nesting calls to ProcessPendingRestyles?");
presContext->SetProcessingRestyles(PR_TRUE);
ProcessPendingRestyleTable(mPendingRestyles);
mPendingRestyles.ProcessRestyles();
#ifdef DEBUG
PRUint32 oldPendingRestyleCount = mPendingRestyles.Count();
@ -11779,7 +11702,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 +11733,9 @@ nsCSSFrameConstructor::PostRestyleEventCommon(Element* aElement,
return;
}
RestyleData existingData;
existingData.mRestyleHint = nsRestyleHint(0);
existingData.mChangeHint = NS_STYLE_HINT_NONE;
nsDataHashtable<nsISupportsHashKey, RestyleData> &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);
}
@ -11835,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);
}
}
@ -11848,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;

View File

@ -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) {
@ -302,17 +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.
void ProcessOneRestyle(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint);
void ProcessPendingRestyleTable(
nsDataHashtable<nsISupportsHashKey, RestyleData>& aRestyles);
friend class mozilla::css::RestyleTracker;
void RestyleForEmptyChange(Element* aContainer);
@ -461,9 +452,9 @@ private:
// XXXbz do we really need the aPrimaryFrame argument here?
void RestyleElement(Element* aElement,
nsIFrame* aPrimaryFrame,
nsChangeHint aMinHint);
void RestyleLaterSiblings(Element* aElement);
nsChangeHint aMinHint,
RestyleTracker& aRestyleTracker,
PRBool aRestyleDescendants);
nsresult InitAndRestoreFrame (const nsFrameConstructorState& aState,
nsIContent* aContent,
@ -1837,15 +1828,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<Element> mElement;
};
friend class nsFrameConstructorState;
private:
@ -1910,8 +1892,8 @@ private:
nsCOMPtr<nsILayoutHistoryState> mTempFrameTreeState;
nsDataHashtable<nsISupportsHashKey, RestyleData> mPendingRestyles;
nsDataHashtable<nsISupportsHashKey, RestyleData> mPendingAnimationRestyles;
RestyleTracker mPendingRestyles;
RestyleTracker mPendingAnimationRestyles;
static nsIXBLService * gXBLService;
};

View File

@ -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_Self = 0x1,
eRestyle_LaterSiblings = 0x2
eRestyle_Subtree = 0x2, /* self and descendants */
eRestyle_LaterSiblings = 0x4 /* implies "and descendants" */
};

View File

@ -91,6 +91,7 @@
#include "nsAutoPtr.h"
#include "imgIRequest.h"
#include "nsTransitionManager.h"
#include "RestyleTracker.h"
#include "nsFrameManager.h"
#ifdef ACCESSIBILITY
@ -961,7 +962,9 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
nsIContent *aParentContent,
nsStyleChangeList *aChangeList,
nsChangeHint aMinChange,
PRBool aFireAccessibilityEvents)
nsRestyleHint aRestyleHint,
PRBool aFireAccessibilityEvents,
RestyleTracker& aRestyleTracker)
{
if (!NS_IsHintSubset(nsChangeHint_NeedDirtyReflow, aMinChange)) {
// If aMinChange doesn't include nsChangeHint_NeedDirtyReflow, clear out
@ -980,7 +983,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
@ -1021,6 +1024,22 @@ 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);
}
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
@ -1049,7 +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);
aMinChange, aRestyleHint,
PR_FALSE, aRestyleTracker);
// The provider's new context becomes the parent context of
// aFrame's context.
@ -1099,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");
@ -1135,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();
@ -1150,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) {
@ -1184,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;
@ -1253,8 +1279,22 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
"Shouldn't have random pseudo style contexts in the "
"undisplayed map");
nsRefPtr<nsStyleContext> undisplayedContext =
styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(), newContext);
nsRestyleHint thisChildHint = childRestyleHint;
RestyleTracker::RestyleData undisplayedRestyleData;
if (aRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
&undisplayedRestyleData)) {
thisChildHint =
nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
}
nsRefPtr<nsStyleContext> undisplayedContext;
if (thisChildHint) {
undisplayedContext =
styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(),
newContext);
} else {
undisplayedContext =
styleSet->ReparentStyleContext(undisplayed->mStyle, newContext);
}
if (undisplayedContext) {
const nsStyleDisplay* display = undisplayedContext->GetStyleDisplay();
if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
@ -1272,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() &&
@ -1296,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() &&
@ -1390,19 +1437,25 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
content, aChangeList,
NS_SubtractHint(aMinChange,
nsChangeHint_ReflowFrame),
fireAccessibilityEvents);
childRestyleHint,
fireAccessibilityEvents,
aRestyleTracker);
// reresolve placeholder's context under the same parent
// as the out-of-flow frame
ReResolveStyleContext(aPresContext, child, content,
aChangeList, aMinChange,
fireAccessibilityEvents);
childRestyleHint,
fireAccessibilityEvents,
aRestyleTracker);
}
else { // regular child frame
if (child != resolvedChild) {
ReResolveStyleContext(aPresContext, child, content,
aChangeList, aMinChange,
fireAccessibilityEvents);
childRestyleHint,
fireAccessibilityEvents,
aRestyleTracker);
} else {
NOISY_TRACE_FRAME("child frame already resolved as descendant, skipping",aFrame);
}
@ -1424,7 +1477,9 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
void
nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame,
nsStyleChangeList *aChangeList,
nsChangeHint aMinChange)
nsChangeHint aMinChange,
RestyleTracker& aRestyleTracker,
PRBool aRestyleDescendants)
{
if (aMinChange) {
aChangeList->AppendChange(aFrame, aFrame->GetContent(), aMinChange);
@ -1449,7 +1504,11 @@ 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);
if (topLevelChange & nsChangeHint_ReconstructFrame) {

View File

@ -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:
* <li>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,9 @@ public:
NS_HIDDEN_(void)
ComputeStyleChangeFor(nsIFrame* aFrame,
nsStyleChangeList* aChangeList,
nsChangeHint aMinChange);
nsChangeHint aMinChange,
RestyleTracker& aRestyleTracker,
PRBool aRestyleDescendants);
/*
* Capture/restore frame state for the frame subtree rooted at aFrame.
@ -192,13 +202,20 @@ 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,
PRBool aFireAccessibilityEvents);
nsRestyleHint aRestyleHint,
PRBool aFireAccessibilityEvents,
RestyleTracker& aRestyleTracker);
};
#endif

View File

@ -67,6 +67,7 @@
#include "mozFlushType.h"
#include "nsWeakReference.h"
#include <stdio.h> // 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.
*/

View File

@ -3441,6 +3441,14 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement,
if (node && ve) {
nsHTMLVideoElement *video = static_cast<nsHTMLVideoElement*>(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<nsIPrincipal> 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,

View File

@ -1144,6 +1144,8 @@ public:
};
struct SurfaceFromElementResult {
SurfaceFromElementResult() : mIsStillLoading(PR_FALSE) {}
/* mSurface will contain the resulting surface, or will be NULL on error */
nsRefPtr<gfxASurface> mSurface;
/* The size of the surface */
@ -1152,6 +1154,9 @@ public:
nsCOMPtr<nsIPrincipal> 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,

View File

@ -3626,13 +3626,18 @@ PresShell::RecreateFramesFor(nsIContent* aContent)
void
nsIPresShell::PostRecreateFramesFor(Element* aElement)
{
FrameConstructor()->PostRestyleEvent(aElement, eRestyle_Self,
FrameConstructor()->PostRestyleEvent(aElement, nsRestyleHint(0),
nsChangeHint_ReconstructFrame);
}
void
nsIPresShell::RestyleForAnimation(Element* aElement)
{
// 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);
}
@ -4902,7 +4907,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 +5084,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);
@ -7744,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
//------------------------------------------------------

View File

@ -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);
}
///////////////////////////////////////////////////////////////////////////

View File

@ -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;

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
This text should be visible
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<style>
body::before { content: inherit; }
</style>
</head>
<body onload="document.body.style.content = '&quot;This text should be visible&quot;';">
</body>
</html>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<head>
<body style="color: green">
This should be green
</body>
</head>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<head>
<style>
body { color: red; }
.Foo { color: green; }
</style>
<body onload="document.body.className = 'abc Foo'" class="abc">
This should be green
</body>
</head>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<head>
<style>
body { color: green; }
.Foo { color: red; }
</style>
<body onload="document.body.className = 'abc foo'" class="Foo">
This should be green
</body>
</head>

View File

@ -0,0 +1,6 @@
<!-- Quirks on purpose -->
<head>
<body style="color: green">
This should be green
</body>
</head>

View File

@ -0,0 +1,10 @@
<!-- Quirks on purpose -->
<head>
<style>
body { color: red; }
.Foo { color: green; }
</style>
<body onload="document.body.className = 'abc Foo'" class="abc">
This should be green
</body>
</head>

View File

@ -0,0 +1,10 @@
<!-- Quirks on purpose -->
<head>
<style>
body { color: green; }
.Foo { color: red; }
</style>
<body onload="document.body.className = 'abc bar'" class="Foo">
This should be green
</body>
</head>

View File

@ -0,0 +1,10 @@
<!-- Quirks on purpose -->
<head>
<style>
body { color: red; }
.Foo { color: green; }
</style>
<body onload="document.body.className = 'abc foO'" class="abc">
This should be green
</body>
</head>

View File

@ -0,0 +1,10 @@
<!-- Quirks on purpose -->
<head>
<style>
body { color: green; }
.Foo { color: red; }
</style>
<body onload="document.body.className = 'abc bar'" class="fOo">
This should be green
</body>
</head>

View File

@ -1444,6 +1444,13 @@ 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
== 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

View File

@ -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 \

View File

@ -706,6 +706,67 @@ static const PLDHashTableOps AttributeSelectorOps = {
};
//--------------------------------
// Class selectors hash table.
struct ClassSelectorEntry : public PLDHashEntryHdr {
nsIAtom *mClass;
nsTArray<nsCSSSelector*> mSelectors;
};
static void
ClassSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
{
(static_cast<ClassSelectorEntry*>(hdr))->~ClassSelectorEntry();
}
static PRBool
ClassSelector_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
const void *key)
{
ClassSelectorEntry *entry = static_cast<ClassSelectorEntry*>(hdr);
new (entry) ClassSelectorEntry();
entry->mClass = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
return PR_TRUE;
}
static nsIAtom*
ClassSelector_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
{
const ClassSelectorEntry *entry = static_cast<const ClassSelectorEntry*>(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<nsCSSSelector*> mStateSelectors;
PRUint32 mSelectorDocumentStates;
nsTArray<nsCSSSelector*> mClassSelectors;
PLDHashTable mClassSelectors;
nsTArray<nsCSSSelector*> mIDSelectors;
PLDHashTable mAttributeSelectors;
PLDHashTable mAnonBoxRules;
@ -2329,10 +2397,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 +2430,7 @@ nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
for(; iter != end; ++iter) {
nsCSSSelector* selector = *iter;
nsRestyleHint possibleChange = IsSiblingOperator(selector->mOperator) ?
eRestyle_LaterSiblings : eRestyle_Self;
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 +2471,7 @@ AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
{
AttributeRuleProcessorData *data = aData->data;
nsRestyleHint possibleChange = IsSiblingOperator(aSelector->mOperator) ?
eRestyle_LaterSiblings : eRestyle_Self;
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
@ -2430,7 +2503,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);
}
}
@ -2452,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<ClassSelectorEntry*>
(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);
}
}
}
}
}
@ -2580,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<ClassSelectorEntry*>(PL_DHashTableOperate(&aCascade->mClassSelectors,
curClass->mAtom,
PL_DHASH_ADD));
if (entry) {
entry->mSelectors.AppendElement(aSelectorInTopLevel);
}
}
// Build mAttributeSelectors.

View File

@ -71,6 +71,7 @@
#include "nsContentErrors.h"
#include "nsRuleProcessorData.h"
#include "mozilla/dom/Element.h"
#include "nsCSSFrameConstructor.h"
using namespace mozilla::dom;
@ -455,52 +456,46 @@ nsHTMLStyleSheet::Reset(nsIURI* aURL)
}
nsresult
nsHTMLStyleSheet::SetLinkColor(nscolor aColor)
nsHTMLStyleSheet::ImplLinkColorSetter(nsRefPtr<HTMLColorRule>& 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<nsMappedAttributes>

View File

@ -127,6 +127,8 @@ private:
nscolor mColor;
};
// Implementation of SetLink/VisitedLink/ActiveLinkColor
nsresult ImplLinkColorSetter(nsRefPtr<HTMLColorRule>& aRule, nscolor aColor);
class GenericTableRule;
friend class GenericTableRule;

View File

@ -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) {

View File

@ -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;
}