layout/base/RestyleManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /**
michael@0 7 * Code responsible for managing style changes: tracking what style
michael@0 8 * changes need to happen, scheduling them, and doing them.
michael@0 9 */
michael@0 10
michael@0 11 #include "RestyleManager.h"
michael@0 12 #include "mozilla/EventStates.h"
michael@0 13 #include "nsLayoutUtils.h"
michael@0 14 #include "GeckoProfiler.h"
michael@0 15 #include "nsStyleChangeList.h"
michael@0 16 #include "nsRuleProcessorData.h"
michael@0 17 #include "nsStyleUtil.h"
michael@0 18 #include "nsCSSFrameConstructor.h"
michael@0 19 #include "nsSVGEffects.h"
michael@0 20 #include "nsCSSRendering.h"
michael@0 21 #include "nsAnimationManager.h"
michael@0 22 #include "nsTransitionManager.h"
michael@0 23 #include "nsViewManager.h"
michael@0 24 #include "nsRenderingContext.h"
michael@0 25 #include "nsSVGIntegrationUtils.h"
michael@0 26 #include "nsCSSAnonBoxes.h"
michael@0 27 #include "nsContainerFrame.h"
michael@0 28 #include "nsPlaceholderFrame.h"
michael@0 29 #include "nsBlockFrame.h"
michael@0 30 #include "nsViewportFrame.h"
michael@0 31 #include "SVGTextFrame.h"
michael@0 32 #include "StickyScrollContainer.h"
michael@0 33 #include "nsIRootBox.h"
michael@0 34 #include "nsIDOMMutationEvent.h"
michael@0 35 #include "nsContentUtils.h"
michael@0 36 #include "nsIFrameInlines.h"
michael@0 37 #include "ActiveLayerTracker.h"
michael@0 38 #include "nsDisplayList.h"
michael@0 39
michael@0 40 #ifdef ACCESSIBILITY
michael@0 41 #include "nsAccessibilityService.h"
michael@0 42 #endif
michael@0 43
michael@0 44 namespace mozilla {
michael@0 45
michael@0 46 using namespace layers;
michael@0 47
michael@0 48 RestyleManager::RestyleManager(nsPresContext* aPresContext)
michael@0 49 : mPresContext(aPresContext)
michael@0 50 , mRebuildAllStyleData(false)
michael@0 51 , mObservingRefreshDriver(false)
michael@0 52 , mInStyleRefresh(false)
michael@0 53 , mHoverGeneration(0)
michael@0 54 , mRebuildAllExtraHint(nsChangeHint(0))
michael@0 55 , mAnimationGeneration(0)
michael@0 56 , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
michael@0 57 ELEMENT_IS_POTENTIAL_RESTYLE_ROOT)
michael@0 58 , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
michael@0 59 ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT)
michael@0 60 {
michael@0 61 mPendingRestyles.Init(this);
michael@0 62 mPendingAnimationRestyles.Init(this);
michael@0 63 }
michael@0 64
michael@0 65 void
michael@0 66 RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
michael@0 67 {
michael@0 68 mOverflowChangedTracker.RemoveFrame(aFrame);
michael@0 69 }
michael@0 70
michael@0 71 #ifdef DEBUG
michael@0 72 // To ensure that the functions below are only called within
michael@0 73 // |ApplyRenderingChangeToTree|.
michael@0 74 static bool gInApplyRenderingChangeToTree = false;
michael@0 75 #endif
michael@0 76
michael@0 77 static void
michael@0 78 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
michael@0 79 nsChangeHint aChange);
michael@0 80
michael@0 81 /**
michael@0 82 * Sync views on aFrame and all of aFrame's descendants (following placeholders),
michael@0 83 * if aChange has nsChangeHint_SyncFrameView.
michael@0 84 * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
michael@0 85 * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
michael@0 86 * aFrame should be some combination of nsChangeHint_SyncFrameView and
michael@0 87 * nsChangeHint_RepaintFrame and nsChangeHint_UpdateOpacityLayer, nothing else.
michael@0 88 */
michael@0 89 static void
michael@0 90 SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
michael@0 91 nsChangeHint aChange)
michael@0 92 {
michael@0 93 NS_PRECONDITION(gInApplyRenderingChangeToTree,
michael@0 94 "should only be called within ApplyRenderingChangeToTree");
michael@0 95 NS_ASSERTION(aChange == (aChange & (nsChangeHint_RepaintFrame |
michael@0 96 nsChangeHint_SyncFrameView |
michael@0 97 nsChangeHint_UpdateOpacityLayer)),
michael@0 98 "Invalid change flag");
michael@0 99
michael@0 100 nsView* view = aFrame->GetView();
michael@0 101 if (view) {
michael@0 102 if (aChange & nsChangeHint_SyncFrameView) {
michael@0 103 nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
michael@0 104 aFrame, nullptr, view);
michael@0 105 }
michael@0 106 }
michael@0 107
michael@0 108 nsIFrame::ChildListIterator lists(aFrame);
michael@0 109 for (; !lists.IsDone(); lists.Next()) {
michael@0 110 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 111 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 112 nsIFrame* child = childFrames.get();
michael@0 113 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
michael@0 114 // only do frames that don't have placeholders
michael@0 115 if (nsGkAtoms::placeholderFrame == child->GetType()) {
michael@0 116 // do the out-of-flow frame and its continuations
michael@0 117 nsIFrame* outOfFlowFrame =
michael@0 118 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
michael@0 119 DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
michael@0 120 } else if (lists.CurrentID() == nsIFrame::kPopupList) {
michael@0 121 DoApplyRenderingChangeToTree(child, aChange);
michael@0 122 } else { // regular frame
michael@0 123 SyncViewsAndInvalidateDescendants(child, aChange);
michael@0 124 }
michael@0 125 }
michael@0 126 }
michael@0 127 }
michael@0 128 }
michael@0 129
michael@0 130 /**
michael@0 131 * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
michael@0 132 * frames of the SVG frame concerned. This helper function is used to find that
michael@0 133 * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
michael@0 134 * that we iterate over the intended children, since sometimes we end up
michael@0 135 * handling that hint while processing hints for one of the SVG frame's
michael@0 136 * ancestor frames.
michael@0 137 *
michael@0 138 * The reason that we sometimes end up trying to process the hint for an
michael@0 139 * ancestor of the SVG frame that the hint is intended for is due to the way we
michael@0 140 * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
michael@0 141 * the restyled element's principle frame to one of its ancestor frames based
michael@0 142 * on what nsCSSRendering::FindBackground returns, since the background style
michael@0 143 * may have been propagated up to an ancestor frame. Processing hints using an
michael@0 144 * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
michael@0 145 * a special case since it is intended to update the children of a specific
michael@0 146 * frame.
michael@0 147 */
michael@0 148 static nsIFrame*
michael@0 149 GetFrameForChildrenOnlyTransformHint(nsIFrame *aFrame)
michael@0 150 {
michael@0 151 if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
michael@0 152 // This happens if the root-<svg> is fixed positioned, in which case we
michael@0 153 // can't use aFrame->GetContent() to find the primary frame, since
michael@0 154 // GetContent() returns nullptr for ViewportFrame.
michael@0 155 aFrame = aFrame->GetFirstPrincipalChild();
michael@0 156 }
michael@0 157 // For an nsHTMLScrollFrame, this will get the SVG frame that has the
michael@0 158 // children-only transforms:
michael@0 159 aFrame = aFrame->GetContent()->GetPrimaryFrame();
michael@0 160 if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
michael@0 161 aFrame = aFrame->GetFirstPrincipalChild();
michael@0 162 NS_ABORT_IF_FALSE(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
michael@0 163 "Where is the nsSVGOuterSVGFrame's anon child??");
michael@0 164 }
michael@0 165 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG |
michael@0 166 nsIFrame::eSVGContainer),
michael@0 167 "Children-only transforms only expected on SVG frames");
michael@0 168 return aFrame;
michael@0 169 }
michael@0 170
michael@0 171 static void
michael@0 172 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
michael@0 173 nsChangeHint aChange)
michael@0 174 {
michael@0 175 NS_PRECONDITION(gInApplyRenderingChangeToTree,
michael@0 176 "should only be called within ApplyRenderingChangeToTree");
michael@0 177
michael@0 178 for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
michael@0 179 // Invalidate and sync views on all descendant frames, following placeholders.
michael@0 180 // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
michael@0 181 // there can't be any out-of-flows or popups that need to be transformed;
michael@0 182 // all out-of-flow descendants of the transformed element must also be
michael@0 183 // descendants of the transformed frame.
michael@0 184 SyncViewsAndInvalidateDescendants(aFrame,
michael@0 185 nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
michael@0 186 nsChangeHint_SyncFrameView |
michael@0 187 nsChangeHint_UpdateOpacityLayer)));
michael@0 188 // This must be set to true if the rendering change needs to
michael@0 189 // invalidate content. If it's false, a composite-only paint
michael@0 190 // (empty transaction) will be scheduled.
michael@0 191 bool needInvalidatingPaint = false;
michael@0 192
michael@0 193 // if frame has view, will already be invalidated
michael@0 194 if (aChange & nsChangeHint_RepaintFrame) {
michael@0 195 // Note that this whole block will be skipped when painting is suppressed
michael@0 196 // (due to our caller ApplyRendingChangeToTree() discarding the
michael@0 197 // nsChangeHint_RepaintFrame hint). If you add handling for any other
michael@0 198 // hints within this block, be sure that they too should be ignored when
michael@0 199 // painting is suppressed.
michael@0 200 needInvalidatingPaint = true;
michael@0 201 aFrame->InvalidateFrameSubtree();
michael@0 202 if (aChange & nsChangeHint_UpdateEffects &&
michael@0 203 aFrame->IsFrameOfType(nsIFrame::eSVG) &&
michael@0 204 !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
michael@0 205 // Need to update our overflow rects:
michael@0 206 nsSVGUtils::ScheduleReflowSVG(aFrame);
michael@0 207 }
michael@0 208 }
michael@0 209 if (aChange & nsChangeHint_UpdateTextPath) {
michael@0 210 if (aFrame->IsSVGText()) {
michael@0 211 // Invalidate and reflow the entire SVGTextFrame:
michael@0 212 NS_ASSERTION(aFrame->GetContent()->IsSVG(nsGkAtoms::textPath),
michael@0 213 "expected frame for a <textPath> element");
michael@0 214 nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
michael@0 215 aFrame,
michael@0 216 nsGkAtoms::svgTextFrame);
michael@0 217 NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
michael@0 218 static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
michael@0 219 } else {
michael@0 220 NS_ABORT_IF_FALSE(false, "unexpected frame got "
michael@0 221 "nsChangeHint_UpdateTextPath");
michael@0 222 }
michael@0 223 }
michael@0 224 if (aChange & nsChangeHint_UpdateOpacityLayer) {
michael@0 225 // FIXME/bug 796697: we can get away with empty transactions for
michael@0 226 // opacity updates in many cases.
michael@0 227 needInvalidatingPaint = true;
michael@0 228
michael@0 229 ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
michael@0 230 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
michael@0 231 // SVG effects paints the opacity without using
michael@0 232 // nsDisplayOpacity. We need to invalidate manually.
michael@0 233 aFrame->InvalidateFrameSubtree();
michael@0 234 }
michael@0 235 }
michael@0 236 if ((aChange & nsChangeHint_UpdateTransformLayer) &&
michael@0 237 aFrame->IsTransformed()) {
michael@0 238 ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
michael@0 239 // If we're not already going to do an invalidating paint, see
michael@0 240 // if we can get away with only updating the transform on a
michael@0 241 // layer for this frame, and not scheduling an invalidating
michael@0 242 // paint.
michael@0 243 if (!needInvalidatingPaint) {
michael@0 244 Layer* layer;
michael@0 245 needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
michael@0 246
michael@0 247 if (!needInvalidatingPaint) {
michael@0 248 // Since we're not going to paint, we need to resend animation
michael@0 249 // data to the layer.
michael@0 250 MOZ_ASSERT(layer, "this can't happen if there's no layer");
michael@0 251 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
michael@0 252 nullptr, nullptr, aFrame, eCSSProperty_transform);
michael@0 253 }
michael@0 254 }
michael@0 255 }
michael@0 256 if (aChange & nsChangeHint_ChildrenOnlyTransform) {
michael@0 257 needInvalidatingPaint = true;
michael@0 258 nsIFrame* childFrame =
michael@0 259 GetFrameForChildrenOnlyTransformHint(aFrame)->GetFirstPrincipalChild();
michael@0 260 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
michael@0 261 ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
michael@0 262 }
michael@0 263 }
michael@0 264 aFrame->SchedulePaint(needInvalidatingPaint ?
michael@0 265 nsIFrame::PAINT_DEFAULT :
michael@0 266 nsIFrame::PAINT_COMPOSITE_ONLY);
michael@0 267 }
michael@0 268 }
michael@0 269
michael@0 270 static void
michael@0 271 ApplyRenderingChangeToTree(nsPresContext* aPresContext,
michael@0 272 nsIFrame* aFrame,
michael@0 273 nsChangeHint aChange)
michael@0 274 {
michael@0 275 // We check StyleDisplay()->HasTransform() in addition to checking
michael@0 276 // IsTransformed() since we can get here for some frames that don't support
michael@0 277 // CSS transforms.
michael@0 278 NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
michael@0 279 aFrame->IsTransformed() ||
michael@0 280 aFrame->StyleDisplay()->HasTransformStyle(),
michael@0 281 "Unexpected UpdateTransformLayer hint");
michael@0 282
michael@0 283 nsIPresShell *shell = aPresContext->PresShell();
michael@0 284 if (shell->IsPaintingSuppressed()) {
michael@0 285 // Don't allow synchronous rendering changes when painting is turned off.
michael@0 286 aChange = NS_SubtractHint(aChange, nsChangeHint_RepaintFrame);
michael@0 287 if (!aChange) {
michael@0 288 return;
michael@0 289 }
michael@0 290 }
michael@0 291
michael@0 292 // If the frame's background is propagated to an ancestor, walk up to
michael@0 293 // that ancestor.
michael@0 294 nsStyleContext *bgSC;
michael@0 295 while (!nsCSSRendering::FindBackground(aFrame, &bgSC)) {
michael@0 296 aFrame = aFrame->GetParent();
michael@0 297 NS_ASSERTION(aFrame, "root frame must paint");
michael@0 298 }
michael@0 299
michael@0 300 // Trigger rendering updates by damaging this frame and any
michael@0 301 // continuations of this frame.
michael@0 302
michael@0 303 // XXX this needs to detect the need for a view due to an opacity change and deal with it...
michael@0 304
michael@0 305 #ifdef DEBUG
michael@0 306 gInApplyRenderingChangeToTree = true;
michael@0 307 #endif
michael@0 308 DoApplyRenderingChangeToTree(aFrame, aChange);
michael@0 309 #ifdef DEBUG
michael@0 310 gInApplyRenderingChangeToTree = false;
michael@0 311 #endif
michael@0 312 }
michael@0 313
michael@0 314 bool
michael@0 315 RestyleManager::RecomputePosition(nsIFrame* aFrame)
michael@0 316 {
michael@0 317 // Don't process position changes on table frames, since we already handle
michael@0 318 // the dynamic position change on the outer table frame, and the reflow-based
michael@0 319 // fallback code path also ignores positions on inner table frames.
michael@0 320 if (aFrame->GetType() == nsGkAtoms::tableFrame) {
michael@0 321 return true;
michael@0 322 }
michael@0 323
michael@0 324 const nsStyleDisplay* display = aFrame->StyleDisplay();
michael@0 325 // Changes to the offsets of a non-positioned element can safely be ignored.
michael@0 326 if (display->mPosition == NS_STYLE_POSITION_STATIC) {
michael@0 327 return true;
michael@0 328 }
michael@0 329
michael@0 330 // Don't process position changes on frames which have views or the ones which
michael@0 331 // have a view somewhere in their descendants, because the corresponding view
michael@0 332 // needs to be repositioned properly as well.
michael@0 333 if (aFrame->HasView() ||
michael@0 334 (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
michael@0 335 StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
michael@0 336 return false;
michael@0 337 }
michael@0 338
michael@0 339 aFrame->SchedulePaint();
michael@0 340
michael@0 341 // For relative positioning, we can simply update the frame rect
michael@0 342 if (display->IsRelativelyPositionedStyle()) {
michael@0 343 if (display->IsInnerTableStyle()) {
michael@0 344 // We don't currently support relative positioning of inner table
michael@0 345 // elements (bug 35168). If we apply offsets to things we haven't
michael@0 346 // previously offset, we'll get confused. So bail.
michael@0 347 return true;
michael@0 348 }
michael@0 349
michael@0 350
michael@0 351 // Move the frame
michael@0 352 if (display->mPosition == NS_STYLE_POSITION_STICKY) {
michael@0 353 // Update sticky positioning for an entire element at once when
michael@0 354 // RecomputePosition is called with the first continuation in a chain.
michael@0 355 StickyScrollContainer::ComputeStickyOffsets(aFrame);
michael@0 356 StickyScrollContainer* ssc =
michael@0 357 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
michael@0 358 if (ssc) {
michael@0 359 ssc->PositionContinuations(aFrame);
michael@0 360 }
michael@0 361 } else {
michael@0 362 MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
michael@0 363 "Unexpected type of positioning");
michael@0 364 for (nsIFrame *cont = aFrame; cont;
michael@0 365 cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
michael@0 366 nsIFrame* cb = cont->GetContainingBlock();
michael@0 367 nsMargin newOffsets;
michael@0 368 const nsSize size = cb->GetContentRectRelativeToSelf().Size();
michael@0 369
michael@0 370 nsHTMLReflowState::ComputeRelativeOffsets(
michael@0 371 cb->StyleVisibility()->mDirection,
michael@0 372 cont, size.width, size.height, newOffsets);
michael@0 373 NS_ASSERTION(newOffsets.left == -newOffsets.right &&
michael@0 374 newOffsets.top == -newOffsets.bottom,
michael@0 375 "ComputeRelativeOffsets should return valid results");
michael@0 376
michael@0 377 // nsHTMLReflowState::ApplyRelativePositioning would work here, but
michael@0 378 // since we've already checked mPosition and aren't changing the frame's
michael@0 379 // normal position, go ahead and add the offsets directly.
michael@0 380 cont->SetPosition(cont->GetNormalPosition() +
michael@0 381 nsPoint(newOffsets.left, newOffsets.top));
michael@0 382 }
michael@0 383 }
michael@0 384
michael@0 385 return true;
michael@0 386 }
michael@0 387
michael@0 388 // For the absolute positioning case, set up a fake HTML reflow state for
michael@0 389 // the frame, and then get the offsets and size from it. If the frame's size
michael@0 390 // doesn't need to change, we can simply update the frame position. Otherwise
michael@0 391 // we fall back to a reflow.
michael@0 392 nsRefPtr<nsRenderingContext> rc =
michael@0 393 aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
michael@0 394
michael@0 395 // Construct a bogus parent reflow state so that there's a usable
michael@0 396 // containing block reflow state.
michael@0 397 nsIFrame* parentFrame = aFrame->GetParent();
michael@0 398 nsSize parentSize = parentFrame->GetSize();
michael@0 399
michael@0 400 nsFrameState savedState = parentFrame->GetStateBits();
michael@0 401 nsHTMLReflowState parentReflowState(aFrame->PresContext(), parentFrame,
michael@0 402 rc, parentSize);
michael@0 403 parentFrame->RemoveStateBits(~nsFrameState(0));
michael@0 404 parentFrame->AddStateBits(savedState);
michael@0 405
michael@0 406 NS_WARN_IF_FALSE(parentSize.width != NS_INTRINSICSIZE &&
michael@0 407 parentSize.height != NS_INTRINSICSIZE,
michael@0 408 "parentSize should be valid");
michael@0 409 parentReflowState.SetComputedWidth(std::max(parentSize.width, 0));
michael@0 410 parentReflowState.SetComputedHeight(std::max(parentSize.height, 0));
michael@0 411 parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
michael@0 412
michael@0 413 parentReflowState.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
michael@0 414 parentReflowState.ComputedPhysicalBorderPadding() =
michael@0 415 parentFrame->GetUsedBorderAndPadding();
michael@0 416 nsSize availSize(parentSize.width, NS_INTRINSICSIZE);
michael@0 417
michael@0 418 ViewportFrame* viewport = do_QueryFrame(parentFrame);
michael@0 419 nsSize cbSize = viewport ?
michael@0 420 viewport->AdjustReflowStateAsContainingBlock(&parentReflowState).Size()
michael@0 421 : aFrame->GetContainingBlock()->GetSize();
michael@0 422 const nsMargin& parentBorder =
michael@0 423 parentReflowState.mStyleBorder->GetComputedBorder();
michael@0 424 cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
michael@0 425 nsHTMLReflowState reflowState(aFrame->PresContext(), parentReflowState,
michael@0 426 aFrame, availSize, cbSize.width,
michael@0 427 cbSize.height);
michael@0 428 nsSize computedSize(reflowState.ComputedWidth(), reflowState.ComputedHeight());
michael@0 429 computedSize.width += reflowState.ComputedPhysicalBorderPadding().LeftRight();
michael@0 430 if (computedSize.height != NS_INTRINSICSIZE) {
michael@0 431 computedSize.height += reflowState.ComputedPhysicalBorderPadding().TopBottom();
michael@0 432 }
michael@0 433 nsSize size = aFrame->GetSize();
michael@0 434 // The RecomputePosition hint is not used if any offset changed between auto
michael@0 435 // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
michael@0 436 // element height will be its intrinsic height, and since 'top' and 'bottom''s
michael@0 437 // auto-ness hasn't changed, the old height must also be its intrinsic
michael@0 438 // height, which we can assume hasn't changed (or reflow would have
michael@0 439 // been triggered).
michael@0 440 if (computedSize.width == size.width &&
michael@0 441 (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
michael@0 442 // If we're solving for 'left' or 'top', then compute it here, in order to
michael@0 443 // match the reflow code path.
michael@0 444 if (NS_AUTOOFFSET == reflowState.ComputedPhysicalOffsets().left) {
michael@0 445 reflowState.ComputedPhysicalOffsets().left = cbSize.width -
michael@0 446 reflowState.ComputedPhysicalOffsets().right -
michael@0 447 reflowState.ComputedPhysicalMargin().right -
michael@0 448 size.width -
michael@0 449 reflowState.ComputedPhysicalMargin().left;
michael@0 450 }
michael@0 451
michael@0 452 if (NS_AUTOOFFSET == reflowState.ComputedPhysicalOffsets().top) {
michael@0 453 reflowState.ComputedPhysicalOffsets().top = cbSize.height -
michael@0 454 reflowState.ComputedPhysicalOffsets().bottom -
michael@0 455 reflowState.ComputedPhysicalMargin().bottom -
michael@0 456 size.height -
michael@0 457 reflowState.ComputedPhysicalMargin().top;
michael@0 458 }
michael@0 459
michael@0 460 // Move the frame
michael@0 461 nsPoint pos(parentBorder.left + reflowState.ComputedPhysicalOffsets().left +
michael@0 462 reflowState.ComputedPhysicalMargin().left,
michael@0 463 parentBorder.top + reflowState.ComputedPhysicalOffsets().top +
michael@0 464 reflowState.ComputedPhysicalMargin().top);
michael@0 465 aFrame->SetPosition(pos);
michael@0 466
michael@0 467 return true;
michael@0 468 }
michael@0 469
michael@0 470 // Fall back to a reflow
michael@0 471 StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
michael@0 472 return false;
michael@0 473 }
michael@0 474
michael@0 475 nsresult
michael@0 476 RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
michael@0 477 {
michael@0 478 nsIPresShell::IntrinsicDirty dirtyType;
michael@0 479 if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
michael@0 480 NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
michael@0 481 "Please read the comments in nsChangeHint.h");
michael@0 482 dirtyType = nsIPresShell::eStyleChange;
michael@0 483 } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
michael@0 484 dirtyType = nsIPresShell::eTreeChange;
michael@0 485 } else {
michael@0 486 dirtyType = nsIPresShell::eResize;
michael@0 487 }
michael@0 488
michael@0 489 nsFrameState dirtyBits;
michael@0 490 if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
michael@0 491 dirtyBits = nsFrameState(0);
michael@0 492 } else if (aHint & nsChangeHint_NeedDirtyReflow) {
michael@0 493 dirtyBits = NS_FRAME_IS_DIRTY;
michael@0 494 } else {
michael@0 495 dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
michael@0 496 }
michael@0 497
michael@0 498 // If we're not going to clear any intrinsic sizes on the frames, and
michael@0 499 // there are no dirty bits to set, then there's nothing to do.
michael@0 500 if (dirtyType == nsIPresShell::eResize && !dirtyBits)
michael@0 501 return NS_OK;
michael@0 502
michael@0 503 do {
michael@0 504 mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits);
michael@0 505 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
michael@0 506 } while (aFrame);
michael@0 507
michael@0 508 return NS_OK;
michael@0 509 }
michael@0 510
michael@0 511 NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
michael@0 512
michael@0 513 /**
michael@0 514 * Return true if aFrame's subtree has placeholders for out-of-flow content
michael@0 515 * whose 'position' style's bit in aPositionMask is set.
michael@0 516 */
michael@0 517 static bool
michael@0 518 FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
michael@0 519 {
michael@0 520 const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
michael@0 521 nsIFrame::kFixedList);
michael@0 522 for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
michael@0 523 if (!skip.Contains(lists.CurrentID())) {
michael@0 524 for (nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 525 !childFrames.AtEnd(); childFrames.Next()) {
michael@0 526 nsIFrame* f = childFrames.get();
michael@0 527 if (f->GetType() == nsGkAtoms::placeholderFrame) {
michael@0 528 nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
michael@0 529 // If SVG text frames could appear here, they could confuse us since
michael@0 530 // they ignore their position style ... but they can't.
michael@0 531 NS_ASSERTION(!outOfFlow->IsSVGText(),
michael@0 532 "SVG text frames can't be out of flow");
michael@0 533 if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
michael@0 534 return true;
michael@0 535 }
michael@0 536 }
michael@0 537 if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
michael@0 538 return true;
michael@0 539 }
michael@0 540 }
michael@0 541 }
michael@0 542 }
michael@0 543 return false;
michael@0 544 }
michael@0 545
michael@0 546 static bool
michael@0 547 NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
michael@0 548 {
michael@0 549 static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
michael@0 550 NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
michael@0 551 static_assert(0 <= NS_STYLE_POSITION_FIXED &&
michael@0 552 NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
michael@0 553
michael@0 554 uint32_t positionMask;
michael@0 555 // Don't call aFrame->IsPositioned here, since that returns true if
michael@0 556 // the frame already has a transform, and we want to ignore that here
michael@0 557 if (aFrame->IsAbsolutelyPositioned() ||
michael@0 558 aFrame->IsRelativelyPositioned()) {
michael@0 559 // This frame is a container for abs-pos descendants whether or not it
michael@0 560 // has a transform.
michael@0 561 // So abs-pos descendants are no problem; we only need to reframe if
michael@0 562 // we have fixed-pos descendants.
michael@0 563 positionMask = 1 << NS_STYLE_POSITION_FIXED;
michael@0 564 } else {
michael@0 565 // This frame may not be a container for abs-pos descendants already.
michael@0 566 // So reframe if we have abs-pos or fixed-pos descendants.
michael@0 567 positionMask = (1 << NS_STYLE_POSITION_FIXED) |
michael@0 568 (1 << NS_STYLE_POSITION_ABSOLUTE);
michael@0 569 }
michael@0 570 for (nsIFrame* f = aFrame; f;
michael@0 571 f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
michael@0 572 if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
michael@0 573 return true;
michael@0 574 }
michael@0 575 }
michael@0 576 return false;
michael@0 577 }
michael@0 578
michael@0 579 nsresult
michael@0 580 RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
michael@0 581 {
michael@0 582 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
michael@0 583 "Someone forgot a script blocker");
michael@0 584 int32_t count = aChangeList.Count();
michael@0 585 if (!count)
michael@0 586 return NS_OK;
michael@0 587
michael@0 588 PROFILER_LABEL("CSS", "ProcessRestyledFrames");
michael@0 589
michael@0 590 // Make sure to not rebuild quote or counter lists while we're
michael@0 591 // processing restyles
michael@0 592 FrameConstructor()->BeginUpdate();
michael@0 593
michael@0 594 FramePropertyTable* propTable = mPresContext->PropertyTable();
michael@0 595
michael@0 596 // Mark frames so that we skip frames that die along the way, bug 123049.
michael@0 597 // A frame can be in the list multiple times with different hints. Further
michael@0 598 // optmization is possible if nsStyleChangeList::AppendChange could coalesce
michael@0 599 int32_t index = count;
michael@0 600
michael@0 601 while (0 <= --index) {
michael@0 602 const nsStyleChangeData* changeData;
michael@0 603 aChangeList.ChangeAt(index, &changeData);
michael@0 604 if (changeData->mFrame) {
michael@0 605 propTable->Set(changeData->mFrame, ChangeListProperty(),
michael@0 606 NS_INT32_TO_PTR(1));
michael@0 607 }
michael@0 608 }
michael@0 609
michael@0 610 index = count;
michael@0 611
michael@0 612 bool didUpdateCursor = false;
michael@0 613
michael@0 614 while (0 <= --index) {
michael@0 615 nsIFrame* frame;
michael@0 616 nsIContent* content;
michael@0 617 bool didReflowThisFrame = false;
michael@0 618 nsChangeHint hint;
michael@0 619 aChangeList.ChangeAt(index, frame, content, hint);
michael@0 620
michael@0 621 NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
michael@0 622 (hint & nsChangeHint_NeedReflow),
michael@0 623 "Reflow hint bits set without actually asking for a reflow");
michael@0 624
michael@0 625 // skip any frame that has been destroyed due to a ripple effect
michael@0 626 if (frame && !propTable->Get(frame, ChangeListProperty())) {
michael@0 627 continue;
michael@0 628 }
michael@0 629
michael@0 630 if (frame && frame->GetContent() != content) {
michael@0 631 // XXXbz this is due to image maps messing with the primary frame of
michael@0 632 // <area>s. See bug 135040. Remove this block once that's fixed.
michael@0 633 frame = nullptr;
michael@0 634 if (!(hint & nsChangeHint_ReconstructFrame)) {
michael@0 635 continue;
michael@0 636 }
michael@0 637 }
michael@0 638
michael@0 639 if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
michael@0 640 !(hint & nsChangeHint_ReconstructFrame)) {
michael@0 641 if (NeedToReframeForAddingOrRemovingTransform(frame) ||
michael@0 642 frame->GetType() == nsGkAtoms::fieldSetFrame ||
michael@0 643 frame->GetContentInsertionFrame() != frame) {
michael@0 644 // The frame has positioned children that need to be reparented, or
michael@0 645 // it can't easily be converted to/from being an abs-pos container correctly.
michael@0 646 NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
michael@0 647 } else {
michael@0 648 for (nsIFrame *cont = frame; cont;
michael@0 649 cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
michael@0 650 // Normally frame construction would set state bits as needed,
michael@0 651 // but we're not going to reconstruct the frame so we need to set them.
michael@0 652 // It's because we need to set this state on each affected frame
michael@0 653 // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up
michael@0 654 // to ancestors (i.e. it can't be an inherited change hint).
michael@0 655 if (cont->IsPositioned()) {
michael@0 656 // If a transform has been added, we'll be taking this path,
michael@0 657 // but we may be taking this path even if a transform has been
michael@0 658 // removed. It's OK to add the bit even if it's not needed.
michael@0 659 cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
michael@0 660 if (!cont->IsAbsoluteContainer() &&
michael@0 661 (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
michael@0 662 cont->MarkAsAbsoluteContainingBlock();
michael@0 663 }
michael@0 664 } else {
michael@0 665 // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
michael@0 666 // transformed by other means. It's OK to have the bit even if it's
michael@0 667 // not needed.
michael@0 668 if (cont->IsAbsoluteContainer()) {
michael@0 669 cont->MarkAsNotAbsoluteContainingBlock();
michael@0 670 }
michael@0 671 }
michael@0 672 }
michael@0 673 }
michael@0 674 }
michael@0 675 if (hint & nsChangeHint_ReconstructFrame) {
michael@0 676 // If we ever start passing true here, be careful of restyles
michael@0 677 // that involve a reframe and animations. In particular, if the
michael@0 678 // restyle we're processing here is an animation restyle, but
michael@0 679 // the style resolution we will do for the frame construction
michael@0 680 // happens async when we're not in an animation restyle already,
michael@0 681 // problems could arise.
michael@0 682 FrameConstructor()->RecreateFramesForContent(content, false);
michael@0 683 } else {
michael@0 684 NS_ASSERTION(frame, "This shouldn't happen");
michael@0 685
michael@0 686 if ((frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
michael@0 687 (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
michael@0 688 // frame does not maintain overflow rects, so avoid calling
michael@0 689 // FinishAndStoreOverflow on it:
michael@0 690 hint = NS_SubtractHint(hint,
michael@0 691 NS_CombineHint(nsChangeHint_UpdateOverflow,
michael@0 692 NS_CombineHint(nsChangeHint_ChildrenOnlyTransform,
michael@0 693 nsChangeHint_UpdatePostTransformOverflow)));
michael@0 694 }
michael@0 695
michael@0 696 if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
michael@0 697 // Frame can not be transformed, and thus a change in transform will
michael@0 698 // have no effect and we should not use the
michael@0 699 // nsChangeHint_UpdatePostTransformOverflow hint.
michael@0 700 hint = NS_SubtractHint(hint, nsChangeHint_UpdatePostTransformOverflow);
michael@0 701 }
michael@0 702
michael@0 703 if (hint & nsChangeHint_UpdateEffects) {
michael@0 704 for (nsIFrame *cont = frame; cont;
michael@0 705 cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
michael@0 706 nsSVGEffects::UpdateEffects(cont);
michael@0 707 }
michael@0 708 }
michael@0 709 if (hint & nsChangeHint_NeedReflow) {
michael@0 710 StyleChangeReflow(frame, hint);
michael@0 711 didReflowThisFrame = true;
michael@0 712 }
michael@0 713 if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
michael@0 714 nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
michael@0 715 nsChangeHint_ChildrenOnlyTransform)) {
michael@0 716 ApplyRenderingChangeToTree(mPresContext, frame, hint);
michael@0 717 }
michael@0 718 if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
michael@0 719 ActiveLayerTracker::NotifyOffsetRestyle(frame);
michael@0 720 // It is possible for this to fall back to a reflow
michael@0 721 if (!RecomputePosition(frame)) {
michael@0 722 didReflowThisFrame = true;
michael@0 723 }
michael@0 724 }
michael@0 725 NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
michael@0 726 (hint & nsChangeHint_UpdateOverflow),
michael@0 727 "nsChangeHint_UpdateOverflow should be passed too");
michael@0 728 if (!didReflowThisFrame &&
michael@0 729 (hint & (nsChangeHint_UpdateOverflow |
michael@0 730 nsChangeHint_UpdatePostTransformOverflow))) {
michael@0 731 OverflowChangedTracker::ChangeKind changeKind;
michael@0 732 if (hint & nsChangeHint_ChildrenOnlyTransform) {
michael@0 733 // The overflow areas of the child frames need to be updated:
michael@0 734 nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
michael@0 735 nsIFrame* childFrame = hintFrame->GetFirstPrincipalChild();
michael@0 736 NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
michael@0 737 "SVG frames should not have continuations "
michael@0 738 "or ib-split siblings");
michael@0 739 NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
michael@0 740 "SVG frames should not have continuations "
michael@0 741 "or ib-split siblings");
michael@0 742 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
michael@0 743 NS_ABORT_IF_FALSE(childFrame->IsFrameOfType(nsIFrame::eSVG),
michael@0 744 "Not expecting non-SVG children");
michael@0 745 // If |childFrame| is dirty or has dirty children, we don't bother
michael@0 746 // updating overflows since that will happen when it's reflowed.
michael@0 747 if (!(childFrame->GetStateBits() &
michael@0 748 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
michael@0 749 mOverflowChangedTracker.AddFrame(childFrame,
michael@0 750 OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
michael@0 751 }
michael@0 752 NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
michael@0 753 "SVG frames should not have continuations "
michael@0 754 "or ib-split siblings");
michael@0 755 NS_ASSERTION(childFrame->GetParent() == hintFrame,
michael@0 756 "SVG child frame not expected to have different parent");
michael@0 757 }
michael@0 758 }
michael@0 759 // If |frame| is dirty or has dirty children, we don't bother updating
michael@0 760 // overflows since that will happen when it's reflowed.
michael@0 761 if (!(frame->GetStateBits() &
michael@0 762 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
michael@0 763 // If we have both nsChangeHint_UpdateOverflow and
michael@0 764 // nsChangeHint_UpdatePostTransformOverflow, CHILDREN_AND_PARENT_CHANGED
michael@0 765 // is selected as it is stronger.
michael@0 766 if (hint & nsChangeHint_UpdateOverflow) {
michael@0 767 changeKind = OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED;
michael@0 768 } else {
michael@0 769 changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
michael@0 770 }
michael@0 771 for (nsIFrame *cont = frame; cont; cont =
michael@0 772 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
michael@0 773 mOverflowChangedTracker.AddFrame(cont, changeKind);
michael@0 774 }
michael@0 775 }
michael@0 776 }
michael@0 777 if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
michael@0 778 mPresContext->PresShell()->SynthesizeMouseMove(false);
michael@0 779 didUpdateCursor = true;
michael@0 780 }
michael@0 781 }
michael@0 782 }
michael@0 783
michael@0 784 FrameConstructor()->EndUpdate();
michael@0 785
michael@0 786 // cleanup references and verify the style tree. Note that the latter needs
michael@0 787 // to happen once we've processed the whole list, since until then the tree
michael@0 788 // is not in fact in a consistent state.
michael@0 789 index = count;
michael@0 790 while (0 <= --index) {
michael@0 791 const nsStyleChangeData* changeData;
michael@0 792 aChangeList.ChangeAt(index, &changeData);
michael@0 793 if (changeData->mFrame) {
michael@0 794 propTable->Delete(changeData->mFrame, ChangeListProperty());
michael@0 795 }
michael@0 796
michael@0 797 #ifdef DEBUG
michael@0 798 // reget frame from content since it may have been regenerated...
michael@0 799 if (changeData->mContent) {
michael@0 800 if (!nsAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent) &&
michael@0 801 !nsTransitionManager::ContentOrAncestorHasTransition(changeData->mContent)) {
michael@0 802 nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
michael@0 803 if (frame) {
michael@0 804 DebugVerifyStyleTree(frame);
michael@0 805 }
michael@0 806 }
michael@0 807 } else if (!changeData->mFrame ||
michael@0 808 changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
michael@0 809 NS_WARNING("Unable to test style tree integrity -- no content node "
michael@0 810 "(and not a viewport frame)");
michael@0 811 }
michael@0 812 #endif
michael@0 813 }
michael@0 814
michael@0 815 aChangeList.Clear();
michael@0 816 return NS_OK;
michael@0 817 }
michael@0 818
michael@0 819 void
michael@0 820 RestyleManager::RestyleElement(Element* aElement,
michael@0 821 nsIFrame* aPrimaryFrame,
michael@0 822 nsChangeHint aMinHint,
michael@0 823 RestyleTracker& aRestyleTracker,
michael@0 824 bool aRestyleDescendants)
michael@0 825 {
michael@0 826 NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
michael@0 827 "frame/content mismatch");
michael@0 828 if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
michael@0 829 // XXXbz this is due to image maps messing with the primary frame pointer
michael@0 830 // of <area>s. See bug 135040. We can remove this block once that's fixed.
michael@0 831 aPrimaryFrame = nullptr;
michael@0 832 }
michael@0 833 NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
michael@0 834 "frame/content mismatch");
michael@0 835
michael@0 836 // If we're restyling the root element and there are 'rem' units in
michael@0 837 // use, handle dynamic changes to the definition of a 'rem' here.
michael@0 838 if (mPresContext->UsesRootEMUnits() && aPrimaryFrame) {
michael@0 839 nsStyleContext *oldContext = aPrimaryFrame->StyleContext();
michael@0 840 if (!oldContext->GetParent()) { // check that we're the root element
michael@0 841 nsRefPtr<nsStyleContext> newContext = mPresContext->StyleSet()->
michael@0 842 ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
michael@0 843 if (oldContext->StyleFont()->mFont.size !=
michael@0 844 newContext->StyleFont()->mFont.size) {
michael@0 845 // The basis for 'rem' units has changed.
michael@0 846 newContext = nullptr;
michael@0 847 DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0));
michael@0 848 if (aMinHint == 0) {
michael@0 849 return;
michael@0 850 }
michael@0 851 aPrimaryFrame = aElement->GetPrimaryFrame();
michael@0 852 }
michael@0 853 }
michael@0 854 }
michael@0 855
michael@0 856 if (aMinHint & nsChangeHint_ReconstructFrame) {
michael@0 857 FrameConstructor()->RecreateFramesForContent(aElement, false);
michael@0 858 } else if (aPrimaryFrame) {
michael@0 859 nsStyleChangeList changeList;
michael@0 860 ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
michael@0 861 aRestyleTracker, aRestyleDescendants);
michael@0 862 ProcessRestyledFrames(changeList);
michael@0 863 } else {
michael@0 864 // no frames, reconstruct for content
michael@0 865 FrameConstructor()->MaybeRecreateFramesForElement(aElement);
michael@0 866 }
michael@0 867 }
michael@0 868
michael@0 869 static inline dom::Element*
michael@0 870 ElementForStyleContext(nsIContent* aParentContent,
michael@0 871 nsIFrame* aFrame,
michael@0 872 nsCSSPseudoElements::Type aPseudoType);
michael@0 873
michael@0 874 // Forwarded nsIDocumentObserver method, to handle restyling (and
michael@0 875 // passing the notification to the frame).
michael@0 876 nsresult
michael@0 877 RestyleManager::ContentStateChanged(nsIContent* aContent,
michael@0 878 EventStates aStateMask)
michael@0 879 {
michael@0 880 // XXXbz it would be good if this function only took Elements, but
michael@0 881 // we'd have to make ESM guarantee that usefully.
michael@0 882 if (!aContent->IsElement()) {
michael@0 883 return NS_OK;
michael@0 884 }
michael@0 885
michael@0 886 Element* aElement = aContent->AsElement();
michael@0 887
michael@0 888 nsStyleSet* styleSet = mPresContext->StyleSet();
michael@0 889 NS_ASSERTION(styleSet, "couldn't get style set");
michael@0 890
michael@0 891 nsChangeHint hint = NS_STYLE_HINT_NONE;
michael@0 892 // Any change to a content state that affects which frames we construct
michael@0 893 // must lead to a frame reconstruct here if we already have a frame.
michael@0 894 // Note that we never decide through non-CSS means to not create frames
michael@0 895 // based on content states, so if we already don't have a frame we don't
michael@0 896 // need to force a reframe -- if it's needed, the HasStateDependentStyle
michael@0 897 // call will handle things.
michael@0 898 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
michael@0 899 nsCSSPseudoElements::Type pseudoType =
michael@0 900 nsCSSPseudoElements::ePseudo_NotPseudoElement;
michael@0 901 if (primaryFrame) {
michael@0 902 // If it's generated content, ignore LOADING/etc state changes on it.
michael@0 903 if (!primaryFrame->IsGeneratedContentFrame() &&
michael@0 904 aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
michael@0 905 NS_EVENT_STATE_USERDISABLED |
michael@0 906 NS_EVENT_STATE_SUPPRESSED |
michael@0 907 NS_EVENT_STATE_LOADING)) {
michael@0 908 hint = nsChangeHint_ReconstructFrame;
michael@0 909 } else {
michael@0 910 uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
michael@0 911 if (app) {
michael@0 912 nsITheme *theme = mPresContext->GetTheme();
michael@0 913 if (theme && theme->ThemeSupportsWidget(mPresContext,
michael@0 914 primaryFrame, app)) {
michael@0 915 bool repaint = false;
michael@0 916 theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint);
michael@0 917 if (repaint) {
michael@0 918 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
michael@0 919 }
michael@0 920 }
michael@0 921 }
michael@0 922 }
michael@0 923
michael@0 924 pseudoType = primaryFrame->StyleContext()->GetPseudoType();
michael@0 925
michael@0 926 primaryFrame->ContentStatesChanged(aStateMask);
michael@0 927 }
michael@0 928
michael@0 929
michael@0 930 nsRestyleHint rshint;
michael@0 931
michael@0 932 if (pseudoType >= nsCSSPseudoElements::ePseudo_PseudoElementCount) {
michael@0 933 rshint = styleSet->HasStateDependentStyle(mPresContext, aElement,
michael@0 934 aStateMask);
michael@0 935 } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
michael@0 936 pseudoType)) {
michael@0 937 // If aElement is a pseudo-element, we want to check to see whether there
michael@0 938 // are any state-dependent rules applying to that pseudo.
michael@0 939 Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
michael@0 940 pseudoType);
michael@0 941 rshint = styleSet->HasStateDependentStyle(mPresContext, ancestor,
michael@0 942 pseudoType, aElement,
michael@0 943 aStateMask);
michael@0 944 } else {
michael@0 945 rshint = nsRestyleHint(0);
michael@0 946 }
michael@0 947
michael@0 948 if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
michael@0 949 ++mHoverGeneration;
michael@0 950 }
michael@0 951
michael@0 952 if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
michael@0 953 // Exposing information to the page about whether the link is
michael@0 954 // visited or not isn't really something we can worry about here.
michael@0 955 // FIXME: We could probably do this a bit better.
michael@0 956 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
michael@0 957 }
michael@0 958
michael@0 959 PostRestyleEvent(aElement, rshint, hint);
michael@0 960 return NS_OK;
michael@0 961 }
michael@0 962
michael@0 963 // Forwarded nsIMutationObserver method, to handle restyling.
michael@0 964 void
michael@0 965 RestyleManager::AttributeWillChange(Element* aElement,
michael@0 966 int32_t aNameSpaceID,
michael@0 967 nsIAtom* aAttribute,
michael@0 968 int32_t aModType)
michael@0 969 {
michael@0 970 nsRestyleHint rshint =
michael@0 971 mPresContext->StyleSet()->HasAttributeDependentStyle(mPresContext,
michael@0 972 aElement,
michael@0 973 aAttribute,
michael@0 974 aModType,
michael@0 975 false);
michael@0 976 PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE);
michael@0 977 }
michael@0 978
michael@0 979 // Forwarded nsIMutationObserver method, to handle restyling (and
michael@0 980 // passing the notification to the frame).
michael@0 981 void
michael@0 982 RestyleManager::AttributeChanged(Element* aElement,
michael@0 983 int32_t aNameSpaceID,
michael@0 984 nsIAtom* aAttribute,
michael@0 985 int32_t aModType)
michael@0 986 {
michael@0 987 // Hold onto the PresShell to prevent ourselves from being destroyed.
michael@0 988 // XXXbz how, exactly, would this attribute change cause us to be
michael@0 989 // destroyed from inside this function?
michael@0 990 nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
michael@0 991
michael@0 992 // Get the frame associated with the content which is the highest in the frame tree
michael@0 993 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
michael@0 994
michael@0 995 #if 0
michael@0 996 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
michael@0 997 ("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
michael@0 998 aContent, ContentTag(aElement, 0), frame));
michael@0 999 #endif
michael@0 1000
michael@0 1001 // the style tag has its own interpretation based on aHint
michael@0 1002 nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
michael@0 1003
michael@0 1004 bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
michael@0 1005
michael@0 1006 #ifdef MOZ_XUL
michael@0 1007 // The following listbox widget trap prevents offscreen listbox widget
michael@0 1008 // content from being removed and re-inserted (which is what would
michael@0 1009 // happen otherwise).
michael@0 1010 if (!primaryFrame && !reframe) {
michael@0 1011 int32_t namespaceID;
michael@0 1012 nsIAtom* tag = mPresContext->Document()->BindingManager()->
michael@0 1013 ResolveTag(aElement, &namespaceID);
michael@0 1014
michael@0 1015 if (namespaceID == kNameSpaceID_XUL &&
michael@0 1016 (tag == nsGkAtoms::listitem ||
michael@0 1017 tag == nsGkAtoms::listcell))
michael@0 1018 return;
michael@0 1019 }
michael@0 1020
michael@0 1021 if (aAttribute == nsGkAtoms::tooltiptext ||
michael@0 1022 aAttribute == nsGkAtoms::tooltip)
michael@0 1023 {
michael@0 1024 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresContext->GetPresShell());
michael@0 1025 if (rootBox) {
michael@0 1026 if (aModType == nsIDOMMutationEvent::REMOVAL)
michael@0 1027 rootBox->RemoveTooltipSupport(aElement);
michael@0 1028 if (aModType == nsIDOMMutationEvent::ADDITION)
michael@0 1029 rootBox->AddTooltipSupport(aElement);
michael@0 1030 }
michael@0 1031 }
michael@0 1032
michael@0 1033 #endif // MOZ_XUL
michael@0 1034
michael@0 1035 if (primaryFrame) {
michael@0 1036 // See if we have appearance information for a theme.
michael@0 1037 const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
michael@0 1038 if (disp->mAppearance) {
michael@0 1039 nsITheme *theme = mPresContext->GetTheme();
michael@0 1040 if (theme && theme->ThemeSupportsWidget(mPresContext, primaryFrame, disp->mAppearance)) {
michael@0 1041 bool repaint = false;
michael@0 1042 theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute, &repaint);
michael@0 1043 if (repaint)
michael@0 1044 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
michael@0 1045 }
michael@0 1046 }
michael@0 1047
michael@0 1048 // let the frame deal with it now, so we don't have to deal later
michael@0 1049 primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
michael@0 1050 // XXXwaterson should probably check for IB split siblings
michael@0 1051 // here, and propagate the AttributeChanged notification to
michael@0 1052 // them, as well. Currently, inline frames don't do anything on
michael@0 1053 // this notification, so it's not that big a deal.
michael@0 1054 }
michael@0 1055
michael@0 1056 // See if we can optimize away the style re-resolution -- must be called after
michael@0 1057 // the frame's AttributeChanged() in case it does something that affects the style
michael@0 1058 nsRestyleHint rshint =
michael@0 1059 mPresContext->StyleSet()->HasAttributeDependentStyle(mPresContext,
michael@0 1060 aElement,
michael@0 1061 aAttribute,
michael@0 1062 aModType,
michael@0 1063 true);
michael@0 1064
michael@0 1065 PostRestyleEvent(aElement, rshint, hint);
michael@0 1066 }
michael@0 1067
michael@0 1068 void
michael@0 1069 RestyleManager::RestyleForEmptyChange(Element* aContainer)
michael@0 1070 {
michael@0 1071 // In some cases (:empty + E, :empty ~ E), a change if the content of
michael@0 1072 // an element requires restyling its parent's siblings.
michael@0 1073 nsRestyleHint hint = eRestyle_Subtree;
michael@0 1074 nsIContent* grandparent = aContainer->GetParent();
michael@0 1075 if (grandparent &&
michael@0 1076 (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
michael@0 1077 hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
michael@0 1078 }
michael@0 1079 PostRestyleEvent(aContainer, hint, NS_STYLE_HINT_NONE);
michael@0 1080 }
michael@0 1081
michael@0 1082 void
michael@0 1083 RestyleManager::RestyleForAppend(Element* aContainer,
michael@0 1084 nsIContent* aFirstNewContent)
michael@0 1085 {
michael@0 1086 NS_ASSERTION(aContainer, "must have container for append");
michael@0 1087 #ifdef DEBUG
michael@0 1088 {
michael@0 1089 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
michael@0 1090 NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
michael@0 1091 "anonymous nodes should not be in child lists");
michael@0 1092 }
michael@0 1093 }
michael@0 1094 #endif
michael@0 1095 uint32_t selectorFlags =
michael@0 1096 aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
michael@0 1097 ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
michael@0 1098 if (selectorFlags == 0)
michael@0 1099 return;
michael@0 1100
michael@0 1101 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
michael@0 1102 // see whether we need to restyle the container
michael@0 1103 bool wasEmpty = true; // :empty or :-moz-only-whitespace
michael@0 1104 for (nsIContent* cur = aContainer->GetFirstChild();
michael@0 1105 cur != aFirstNewContent;
michael@0 1106 cur = cur->GetNextSibling()) {
michael@0 1107 // We don't know whether we're testing :empty or :-moz-only-whitespace,
michael@0 1108 // so be conservative and assume :-moz-only-whitespace (i.e., make
michael@0 1109 // IsSignificantChild less likely to be true, and thus make us more
michael@0 1110 // likely to restyle).
michael@0 1111 if (nsStyleUtil::IsSignificantChild(cur, true, false)) {
michael@0 1112 wasEmpty = false;
michael@0 1113 break;
michael@0 1114 }
michael@0 1115 }
michael@0 1116 if (wasEmpty) {
michael@0 1117 RestyleForEmptyChange(aContainer);
michael@0 1118 return;
michael@0 1119 }
michael@0 1120 }
michael@0 1121
michael@0 1122 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
michael@0 1123 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
michael@0 1124 // Restyling the container is the most we can do here, so we're done.
michael@0 1125 return;
michael@0 1126 }
michael@0 1127
michael@0 1128 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
michael@0 1129 // restyle the last element child before this node
michael@0 1130 for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
michael@0 1131 cur;
michael@0 1132 cur = cur->GetPreviousSibling()) {
michael@0 1133 if (cur->IsElement()) {
michael@0 1134 PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
michael@0 1135 break;
michael@0 1136 }
michael@0 1137 }
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 // Needed since we can't use PostRestyleEvent on non-elements (with
michael@0 1142 // eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
michael@0 1143 // eRestyle_LaterSiblings) as appropriate).
michael@0 1144 static void
michael@0 1145 RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
michael@0 1146 nsIContent* aStartingSibling /* may be null */)
michael@0 1147 {
michael@0 1148 for (nsIContent *sibling = aStartingSibling; sibling;
michael@0 1149 sibling = sibling->GetNextSibling()) {
michael@0 1150 if (sibling->IsElement()) {
michael@0 1151 aRestyleManager->
michael@0 1152 PostRestyleEvent(sibling->AsElement(),
michael@0 1153 nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
michael@0 1154 NS_STYLE_HINT_NONE);
michael@0 1155 break;
michael@0 1156 }
michael@0 1157 }
michael@0 1158 }
michael@0 1159
michael@0 1160 // Restyling for a ContentInserted or CharacterDataChanged notification.
michael@0 1161 // This could be used for ContentRemoved as well if we got the
michael@0 1162 // notification before the removal happened (and sometimes
michael@0 1163 // CharacterDataChanged is more like a removal than an addition).
michael@0 1164 // The comments are written and variables are named in terms of it being
michael@0 1165 // a ContentInserted notification.
michael@0 1166 void
michael@0 1167 RestyleManager::RestyleForInsertOrChange(Element* aContainer,
michael@0 1168 nsIContent* aChild)
michael@0 1169 {
michael@0 1170 NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
michael@0 1171 "anonymous nodes should not be in child lists");
michael@0 1172 uint32_t selectorFlags =
michael@0 1173 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
michael@0 1174 if (selectorFlags == 0)
michael@0 1175 return;
michael@0 1176
michael@0 1177 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
michael@0 1178 // see whether we need to restyle the container
michael@0 1179 bool wasEmpty = true; // :empty or :-moz-only-whitespace
michael@0 1180 for (nsIContent* child = aContainer->GetFirstChild();
michael@0 1181 child;
michael@0 1182 child = child->GetNextSibling()) {
michael@0 1183 if (child == aChild)
michael@0 1184 continue;
michael@0 1185 // We don't know whether we're testing :empty or :-moz-only-whitespace,
michael@0 1186 // so be conservative and assume :-moz-only-whitespace (i.e., make
michael@0 1187 // IsSignificantChild less likely to be true, and thus make us more
michael@0 1188 // likely to restyle).
michael@0 1189 if (nsStyleUtil::IsSignificantChild(child, true, false)) {
michael@0 1190 wasEmpty = false;
michael@0 1191 break;
michael@0 1192 }
michael@0 1193 }
michael@0 1194 if (wasEmpty) {
michael@0 1195 RestyleForEmptyChange(aContainer);
michael@0 1196 return;
michael@0 1197 }
michael@0 1198 }
michael@0 1199
michael@0 1200 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
michael@0 1201 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
michael@0 1202 // Restyling the container is the most we can do here, so we're done.
michael@0 1203 return;
michael@0 1204 }
michael@0 1205
michael@0 1206 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
michael@0 1207 // Restyle all later siblings.
michael@0 1208 RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
michael@0 1209 }
michael@0 1210
michael@0 1211 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
michael@0 1212 // restyle the previously-first element child if it is after this node
michael@0 1213 bool passedChild = false;
michael@0 1214 for (nsIContent* content = aContainer->GetFirstChild();
michael@0 1215 content;
michael@0 1216 content = content->GetNextSibling()) {
michael@0 1217 if (content == aChild) {
michael@0 1218 passedChild = true;
michael@0 1219 continue;
michael@0 1220 }
michael@0 1221 if (content->IsElement()) {
michael@0 1222 if (passedChild) {
michael@0 1223 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
michael@0 1224 NS_STYLE_HINT_NONE);
michael@0 1225 }
michael@0 1226 break;
michael@0 1227 }
michael@0 1228 }
michael@0 1229 // restyle the previously-last element child if it is before this node
michael@0 1230 passedChild = false;
michael@0 1231 for (nsIContent* content = aContainer->GetLastChild();
michael@0 1232 content;
michael@0 1233 content = content->GetPreviousSibling()) {
michael@0 1234 if (content == aChild) {
michael@0 1235 passedChild = true;
michael@0 1236 continue;
michael@0 1237 }
michael@0 1238 if (content->IsElement()) {
michael@0 1239 if (passedChild) {
michael@0 1240 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
michael@0 1241 NS_STYLE_HINT_NONE);
michael@0 1242 }
michael@0 1243 break;
michael@0 1244 }
michael@0 1245 }
michael@0 1246 }
michael@0 1247 }
michael@0 1248
michael@0 1249 void
michael@0 1250 RestyleManager::RestyleForRemove(Element* aContainer,
michael@0 1251 nsIContent* aOldChild,
michael@0 1252 nsIContent* aFollowingSibling)
michael@0 1253 {
michael@0 1254 if (aOldChild->IsRootOfAnonymousSubtree()) {
michael@0 1255 // This should be an assert, but this is called incorrectly in
michael@0 1256 // nsHTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
michael@0 1257 // up the logs. Make it an assert again when that's fixed.
michael@0 1258 NS_WARNING("anonymous nodes should not be in child lists (bug 439258)");
michael@0 1259 }
michael@0 1260 uint32_t selectorFlags =
michael@0 1261 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
michael@0 1262 if (selectorFlags == 0)
michael@0 1263 return;
michael@0 1264
michael@0 1265 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
michael@0 1266 // see whether we need to restyle the container
michael@0 1267 bool isEmpty = true; // :empty or :-moz-only-whitespace
michael@0 1268 for (nsIContent* child = aContainer->GetFirstChild();
michael@0 1269 child;
michael@0 1270 child = child->GetNextSibling()) {
michael@0 1271 // We don't know whether we're testing :empty or :-moz-only-whitespace,
michael@0 1272 // so be conservative and assume :-moz-only-whitespace (i.e., make
michael@0 1273 // IsSignificantChild less likely to be true, and thus make us more
michael@0 1274 // likely to restyle).
michael@0 1275 if (nsStyleUtil::IsSignificantChild(child, true, false)) {
michael@0 1276 isEmpty = false;
michael@0 1277 break;
michael@0 1278 }
michael@0 1279 }
michael@0 1280 if (isEmpty) {
michael@0 1281 RestyleForEmptyChange(aContainer);
michael@0 1282 return;
michael@0 1283 }
michael@0 1284 }
michael@0 1285
michael@0 1286 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
michael@0 1287 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
michael@0 1288 // Restyling the container is the most we can do here, so we're done.
michael@0 1289 return;
michael@0 1290 }
michael@0 1291
michael@0 1292 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
michael@0 1293 // Restyle all later siblings.
michael@0 1294 RestyleSiblingsStartingWith(this, aFollowingSibling);
michael@0 1295 }
michael@0 1296
michael@0 1297 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
michael@0 1298 // restyle the now-first element child if it was after aOldChild
michael@0 1299 bool reachedFollowingSibling = false;
michael@0 1300 for (nsIContent* content = aContainer->GetFirstChild();
michael@0 1301 content;
michael@0 1302 content = content->GetNextSibling()) {
michael@0 1303 if (content == aFollowingSibling) {
michael@0 1304 reachedFollowingSibling = true;
michael@0 1305 // do NOT continue here; we might want to restyle this node
michael@0 1306 }
michael@0 1307 if (content->IsElement()) {
michael@0 1308 if (reachedFollowingSibling) {
michael@0 1309 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
michael@0 1310 NS_STYLE_HINT_NONE);
michael@0 1311 }
michael@0 1312 break;
michael@0 1313 }
michael@0 1314 }
michael@0 1315 // restyle the now-last element child if it was before aOldChild
michael@0 1316 reachedFollowingSibling = (aFollowingSibling == nullptr);
michael@0 1317 for (nsIContent* content = aContainer->GetLastChild();
michael@0 1318 content;
michael@0 1319 content = content->GetPreviousSibling()) {
michael@0 1320 if (content->IsElement()) {
michael@0 1321 if (reachedFollowingSibling) {
michael@0 1322 PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
michael@0 1323 }
michael@0 1324 break;
michael@0 1325 }
michael@0 1326 if (content == aFollowingSibling) {
michael@0 1327 reachedFollowingSibling = true;
michael@0 1328 }
michael@0 1329 }
michael@0 1330 }
michael@0 1331 }
michael@0 1332
michael@0 1333 void
michael@0 1334 RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
michael@0 1335 {
michael@0 1336 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
michael@0 1337 "Should not reconstruct the root of the frame tree. "
michael@0 1338 "Use ReconstructDocElementHierarchy instead.");
michael@0 1339
michael@0 1340 mRebuildAllStyleData = false;
michael@0 1341 NS_UpdateHint(aExtraHint, mRebuildAllExtraHint);
michael@0 1342 mRebuildAllExtraHint = nsChangeHint(0);
michael@0 1343
michael@0 1344 nsIPresShell* presShell = mPresContext->GetPresShell();
michael@0 1345 if (!presShell || !presShell->GetRootFrame())
michael@0 1346 return;
michael@0 1347
michael@0 1348 // Make sure that the viewmanager will outlive the presshell
michael@0 1349 nsRefPtr<nsViewManager> vm = presShell->GetViewManager();
michael@0 1350
michael@0 1351 // Processing the style changes could cause a flush that propagates to
michael@0 1352 // the parent frame and thus destroys the pres shell.
michael@0 1353 nsCOMPtr<nsIPresShell> kungFuDeathGrip(presShell);
michael@0 1354
michael@0 1355 // We may reconstruct frames below and hence process anything that is in the
michael@0 1356 // tree. We don't want to get notified to process those items again after.
michael@0 1357 presShell->GetDocument()->FlushPendingNotifications(Flush_ContentAndNotify);
michael@0 1358
michael@0 1359 nsAutoScriptBlocker scriptBlocker;
michael@0 1360
michael@0 1361 mPresContext->SetProcessingRestyles(true);
michael@0 1362
michael@0 1363 DoRebuildAllStyleData(mPendingRestyles, aExtraHint);
michael@0 1364
michael@0 1365 mPresContext->SetProcessingRestyles(false);
michael@0 1366
michael@0 1367 // Make sure that we process any pending animation restyles from the
michael@0 1368 // above style change. Note that we can *almost* implement the above
michael@0 1369 // by just posting a style change -- except we really need to restyle
michael@0 1370 // the root frame rather than the root element's primary frame.
michael@0 1371 ProcessPendingRestyles();
michael@0 1372 }
michael@0 1373
michael@0 1374 void
michael@0 1375 RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
michael@0 1376 nsChangeHint aExtraHint)
michael@0 1377 {
michael@0 1378 // Tell the style set to get the old rule tree out of the way
michael@0 1379 // so we can recalculate while maintaining rule tree immutability
michael@0 1380 nsresult rv = mPresContext->StyleSet()->BeginReconstruct();
michael@0 1381 if (NS_FAILED(rv)) {
michael@0 1382 return;
michael@0 1383 }
michael@0 1384
michael@0 1385 // Recalculate all of the style contexts for the document
michael@0 1386 // Note that we can ignore the return value of ComputeStyleChangeFor
michael@0 1387 // because we never need to reframe the root frame
michael@0 1388 // XXX This could be made faster by not rerunning rule matching
michael@0 1389 // (but note that nsPresShell::SetPreferenceStyleRules currently depends
michael@0 1390 // on us re-running rule matching here
michael@0 1391 nsStyleChangeList changeList;
michael@0 1392 // XXX Does it matter that we're passing aExtraHint to the real root
michael@0 1393 // frame and not the root node's primary frame?
michael@0 1394 // Note: The restyle tracker we pass in here doesn't matter.
michael@0 1395 ComputeStyleChangeFor(mPresContext->PresShell()->GetRootFrame(),
michael@0 1396 &changeList, aExtraHint,
michael@0 1397 aRestyleTracker, true);
michael@0 1398 // Process the required changes
michael@0 1399 ProcessRestyledFrames(changeList);
michael@0 1400 FlushOverflowChangedTracker();
michael@0 1401
michael@0 1402 // Tell the style set it's safe to destroy the old rule tree. We
michael@0 1403 // must do this after the ProcessRestyledFrames call in case the
michael@0 1404 // change list has frame reconstructs in it (since frames to be
michael@0 1405 // reconstructed will still have their old style context pointers
michael@0 1406 // until they are destroyed).
michael@0 1407 mPresContext->StyleSet()->EndReconstruct();
michael@0 1408 }
michael@0 1409
michael@0 1410 void
michael@0 1411 RestyleManager::ProcessPendingRestyles()
michael@0 1412 {
michael@0 1413 NS_PRECONDITION(mPresContext->Document(), "No document? Pshaw!");
michael@0 1414 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
michael@0 1415 "Missing a script blocker!");
michael@0 1416
michael@0 1417 // First do any queued-up frame creation. (We should really
michael@0 1418 // merge this into the rest of the process, though; see bug 827239.)
michael@0 1419 mPresContext->FrameConstructor()->CreateNeededFrames();
michael@0 1420
michael@0 1421 // Process non-animation restyles...
michael@0 1422 NS_ABORT_IF_FALSE(!mPresContext->IsProcessingRestyles(),
michael@0 1423 "Nesting calls to ProcessPendingRestyles?");
michael@0 1424 mPresContext->SetProcessingRestyles(true);
michael@0 1425
michael@0 1426 // Before we process any restyles, we need to ensure that style
michael@0 1427 // resulting from any throttled animations (animations that we're
michael@0 1428 // running entirely on the compositor thread) is up-to-date, so that
michael@0 1429 // if any style changes we cause trigger transitions, we have the
michael@0 1430 // correct old style for starting the transition.
michael@0 1431 if (nsLayoutUtils::AreAsyncAnimationsEnabled() &&
michael@0 1432 mPendingRestyles.Count() > 0) {
michael@0 1433 ++mAnimationGeneration;
michael@0 1434 mPresContext->TransitionManager()->UpdateAllThrottledStyles();
michael@0 1435 }
michael@0 1436
michael@0 1437 mPendingRestyles.ProcessRestyles();
michael@0 1438
michael@0 1439 #ifdef DEBUG
michael@0 1440 uint32_t oldPendingRestyleCount = mPendingRestyles.Count();
michael@0 1441 #endif
michael@0 1442
michael@0 1443 // ...and then process animation restyles. This needs to happen
michael@0 1444 // second because we need to start animations that resulted from the
michael@0 1445 // first set of restyles (e.g., CSS transitions with negative
michael@0 1446 // transition-delay), and because we need to immediately
michael@0 1447 // restyle-with-animation any just-restyled elements that are
michael@0 1448 // mid-transition (since processing the non-animation restyle ignores
michael@0 1449 // the running transition so it can check for a new change on the same
michael@0 1450 // property, and then posts an immediate animation style change).
michael@0 1451 mPresContext->SetProcessingAnimationStyleChange(true);
michael@0 1452 mPendingAnimationRestyles.ProcessRestyles();
michael@0 1453 mPresContext->SetProcessingAnimationStyleChange(false);
michael@0 1454
michael@0 1455 mPresContext->SetProcessingRestyles(false);
michael@0 1456 NS_POSTCONDITION(mPendingRestyles.Count() == oldPendingRestyleCount,
michael@0 1457 "We should not have posted new non-animation restyles while "
michael@0 1458 "processing animation restyles");
michael@0 1459
michael@0 1460 if (mRebuildAllStyleData) {
michael@0 1461 // We probably wasted a lot of work up above, but this seems safest
michael@0 1462 // and it should be rarely used.
michael@0 1463 // This might add us as a refresh observer again; that's ok.
michael@0 1464 RebuildAllStyleData(nsChangeHint(0));
michael@0 1465 }
michael@0 1466 }
michael@0 1467
michael@0 1468 void
michael@0 1469 RestyleManager::BeginProcessingRestyles()
michael@0 1470 {
michael@0 1471 // Make sure to not rebuild quote or counter lists while we're
michael@0 1472 // processing restyles
michael@0 1473 mPresContext->FrameConstructor()->BeginUpdate();
michael@0 1474
michael@0 1475 mInStyleRefresh = true;
michael@0 1476 }
michael@0 1477
michael@0 1478 void
michael@0 1479 RestyleManager::EndProcessingRestyles()
michael@0 1480 {
michael@0 1481 FlushOverflowChangedTracker();
michael@0 1482
michael@0 1483 // Set mInStyleRefresh to false now, since the EndUpdate call might
michael@0 1484 // add more restyles.
michael@0 1485 mInStyleRefresh = false;
michael@0 1486
michael@0 1487 mPresContext->FrameConstructor()->EndUpdate();
michael@0 1488
michael@0 1489 #ifdef DEBUG
michael@0 1490 mPresContext->PresShell()->VerifyStyleTree();
michael@0 1491 #endif
michael@0 1492 }
michael@0 1493
michael@0 1494 void
michael@0 1495 RestyleManager::PostRestyleEventCommon(Element* aElement,
michael@0 1496 nsRestyleHint aRestyleHint,
michael@0 1497 nsChangeHint aMinChangeHint,
michael@0 1498 bool aForAnimation)
michael@0 1499 {
michael@0 1500 if (MOZ_UNLIKELY(mPresContext->PresShell()->IsDestroying())) {
michael@0 1501 return;
michael@0 1502 }
michael@0 1503
michael@0 1504 if (aRestyleHint == 0 && !aMinChangeHint) {
michael@0 1505 // Nothing to do here
michael@0 1506 return;
michael@0 1507 }
michael@0 1508
michael@0 1509 RestyleTracker& tracker =
michael@0 1510 aForAnimation ? mPendingAnimationRestyles : mPendingRestyles;
michael@0 1511 tracker.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint);
michael@0 1512
michael@0 1513 PostRestyleEventInternal(false);
michael@0 1514 }
michael@0 1515
michael@0 1516 void
michael@0 1517 RestyleManager::PostRestyleEventInternal(bool aForLazyConstruction)
michael@0 1518 {
michael@0 1519 // Make sure we're not in a style refresh; if we are, we still have
michael@0 1520 // a call to ProcessPendingRestyles coming and there's no need to
michael@0 1521 // add ourselves as a refresh observer until then.
michael@0 1522 bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
michael@0 1523 nsIPresShell* presShell = mPresContext->PresShell();
michael@0 1524 if (!mObservingRefreshDriver && !inRefresh) {
michael@0 1525 mObservingRefreshDriver = mPresContext->RefreshDriver()->
michael@0 1526 AddStyleFlushObserver(presShell);
michael@0 1527 }
michael@0 1528
michael@0 1529 // Unconditionally flag our document as needing a flush. The other
michael@0 1530 // option here would be a dedicated boolean to track whether we need
michael@0 1531 // to do so (set here and unset in ProcessPendingRestyles).
michael@0 1532 presShell->GetDocument()->SetNeedStyleFlush();
michael@0 1533 }
michael@0 1534
michael@0 1535 void
michael@0 1536 RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
michael@0 1537 {
michael@0 1538 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
michael@0 1539 "Should not reconstruct the root of the frame tree. "
michael@0 1540 "Use ReconstructDocElementHierarchy instead.");
michael@0 1541
michael@0 1542 mRebuildAllStyleData = true;
michael@0 1543 NS_UpdateHint(mRebuildAllExtraHint, aExtraHint);
michael@0 1544
michael@0 1545 // Get a restyle event posted if necessary
michael@0 1546 PostRestyleEventInternal(false);
michael@0 1547 }
michael@0 1548
michael@0 1549 #ifdef DEBUG
michael@0 1550 static void
michael@0 1551 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
michael@0 1552 {
michael@0 1553 if (aFrame) {
michael@0 1554 fputs("frame: ", stdout);
michael@0 1555 nsAutoString name;
michael@0 1556 aFrame->GetFrameName(name);
michael@0 1557 fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
michael@0 1558 fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
michael@0 1559 }
michael@0 1560 if (aContext) {
michael@0 1561 fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
michael@0 1562
michael@0 1563 nsIAtom* pseudoTag = aContext->GetPseudo();
michael@0 1564 if (pseudoTag) {
michael@0 1565 nsAutoString buffer;
michael@0 1566 pseudoTag->ToString(buffer);
michael@0 1567 fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
michael@0 1568 fputs(" ", stdout);
michael@0 1569 }
michael@0 1570 fputs("{}\n", stdout);
michael@0 1571 }
michael@0 1572 }
michael@0 1573
michael@0 1574 static void
michael@0 1575 VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
michael@0 1576 {
michael@0 1577 nsStyleContext* top1 = aContext1;
michael@0 1578 nsStyleContext* top2 = aContext2;
michael@0 1579 nsStyleContext* parent;
michael@0 1580 for (;;) {
michael@0 1581 parent = top1->GetParent();
michael@0 1582 if (!parent)
michael@0 1583 break;
michael@0 1584 top1 = parent;
michael@0 1585 }
michael@0 1586 for (;;) {
michael@0 1587 parent = top2->GetParent();
michael@0 1588 if (!parent)
michael@0 1589 break;
michael@0 1590 top2 = parent;
michael@0 1591 }
michael@0 1592 NS_ASSERTION(top1 == top2,
michael@0 1593 "Style contexts are not in the same style context tree");
michael@0 1594 }
michael@0 1595
michael@0 1596 static void
michael@0 1597 VerifyContextParent(nsPresContext* aPresContext, nsIFrame* aFrame,
michael@0 1598 nsStyleContext* aContext, nsStyleContext* aParentContext)
michael@0 1599 {
michael@0 1600 // get the contexts not provided
michael@0 1601 if (!aContext) {
michael@0 1602 aContext = aFrame->StyleContext();
michael@0 1603 }
michael@0 1604
michael@0 1605 if (!aParentContext) {
michael@0 1606 // Get the correct parent context from the frame
michael@0 1607 // - if the frame is a placeholder, we get the out of flow frame's context
michael@0 1608 // as the parent context instead of asking the frame
michael@0 1609
michael@0 1610 // get the parent context from the frame (indirectly)
michael@0 1611 nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
michael@0 1612 if (providerFrame)
michael@0 1613 aParentContext = providerFrame->StyleContext();
michael@0 1614 // aParentContext could still be null
michael@0 1615 }
michael@0 1616
michael@0 1617 NS_ASSERTION(aContext, "Failure to get required contexts");
michael@0 1618 nsStyleContext* actualParentContext = aContext->GetParent();
michael@0 1619
michael@0 1620 if (aParentContext) {
michael@0 1621 if (aParentContext != actualParentContext) {
michael@0 1622 DumpContext(aFrame, aContext);
michael@0 1623 if (aContext == aParentContext) {
michael@0 1624 NS_ERROR("Using parent's style context");
michael@0 1625 }
michael@0 1626 else {
michael@0 1627 NS_ERROR("Wrong parent style context");
michael@0 1628 fputs("Wrong parent style context: ", stdout);
michael@0 1629 DumpContext(nullptr, actualParentContext);
michael@0 1630 fputs("should be using: ", stdout);
michael@0 1631 DumpContext(nullptr, aParentContext);
michael@0 1632 VerifySameTree(actualParentContext, aParentContext);
michael@0 1633 fputs("\n", stdout);
michael@0 1634 }
michael@0 1635 }
michael@0 1636
michael@0 1637 }
michael@0 1638 else {
michael@0 1639 if (actualParentContext) {
michael@0 1640 NS_ERROR("Have parent context and shouldn't");
michael@0 1641 DumpContext(aFrame, aContext);
michael@0 1642 fputs("Has parent context: ", stdout);
michael@0 1643 DumpContext(nullptr, actualParentContext);
michael@0 1644 fputs("Should be null\n\n", stdout);
michael@0 1645 }
michael@0 1646 }
michael@0 1647
michael@0 1648 nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
michael@0 1649 // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
michael@0 1650 // as the parent or it has a different rulenode from aContext _and_ has
michael@0 1651 // aContext->GetParent() as the parent.
michael@0 1652 if (childStyleIfVisited &&
michael@0 1653 !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
michael@0 1654 childStyleIfVisited->GetParent() == aContext->GetParent()) ||
michael@0 1655 childStyleIfVisited->GetParent() ==
michael@0 1656 aContext->GetParent()->GetStyleIfVisited())) {
michael@0 1657 NS_ERROR("Visited style has wrong parent");
michael@0 1658 DumpContext(aFrame, aContext);
michael@0 1659 fputs("\n", stdout);
michael@0 1660 }
michael@0 1661 }
michael@0 1662
michael@0 1663 static void
michael@0 1664 VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
michael@0 1665 nsStyleContext* aParentContext)
michael@0 1666 {
michael@0 1667 nsStyleContext* context = aFrame->StyleContext();
michael@0 1668 VerifyContextParent(aPresContext, aFrame, context, nullptr);
michael@0 1669
michael@0 1670 nsIFrame::ChildListIterator lists(aFrame);
michael@0 1671 for (; !lists.IsDone(); lists.Next()) {
michael@0 1672 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 1673 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 1674 nsIFrame* child = childFrames.get();
michael@0 1675 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
michael@0 1676 // only do frames that are in flow
michael@0 1677 if (nsGkAtoms::placeholderFrame == child->GetType()) {
michael@0 1678 // placeholder: first recurse and verify the out of flow frame,
michael@0 1679 // then verify the placeholder's context
michael@0 1680 nsIFrame* outOfFlowFrame =
michael@0 1681 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
michael@0 1682
michael@0 1683 // recurse to out of flow frame, letting the parent context get resolved
michael@0 1684 do {
michael@0 1685 VerifyStyleTree(aPresContext, outOfFlowFrame, nullptr);
michael@0 1686 } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
michael@0 1687
michael@0 1688 // verify placeholder using the parent frame's context as
michael@0 1689 // parent context
michael@0 1690 VerifyContextParent(aPresContext, child, nullptr, nullptr);
michael@0 1691 }
michael@0 1692 else { // regular frame
michael@0 1693 VerifyStyleTree(aPresContext, child, nullptr);
michael@0 1694 }
michael@0 1695 }
michael@0 1696 }
michael@0 1697 }
michael@0 1698
michael@0 1699 // do additional contexts
michael@0 1700 int32_t contextIndex = 0;
michael@0 1701 for (nsStyleContext* extraContext;
michael@0 1702 (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
michael@0 1703 ++contextIndex) {
michael@0 1704 VerifyContextParent(aPresContext, aFrame, extraContext, context);
michael@0 1705 }
michael@0 1706 }
michael@0 1707
michael@0 1708 void
michael@0 1709 RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
michael@0 1710 {
michael@0 1711 if (aFrame) {
michael@0 1712 nsStyleContext* context = aFrame->StyleContext();
michael@0 1713 nsStyleContext* parentContext = context->GetParent();
michael@0 1714 VerifyStyleTree(mPresContext, aFrame, parentContext);
michael@0 1715 }
michael@0 1716 }
michael@0 1717
michael@0 1718 #endif // DEBUG
michael@0 1719
michael@0 1720 // aContent must be the content for the frame in question, which may be
michael@0 1721 // :before/:after content
michael@0 1722 static void
michael@0 1723 TryStartingTransition(nsPresContext *aPresContext, nsIContent *aContent,
michael@0 1724 nsStyleContext *aOldStyleContext,
michael@0 1725 nsRefPtr<nsStyleContext> *aNewStyleContext /* inout */)
michael@0 1726 {
michael@0 1727 if (!aContent || !aContent->IsElement()) {
michael@0 1728 return;
michael@0 1729 }
michael@0 1730
michael@0 1731 // Notify the transition manager, and if it starts a transition,
michael@0 1732 // it will give us back a transition-covering style rule which
michael@0 1733 // we'll use to get *another* style context. We want to ignore
michael@0 1734 // any already-running transitions, but cover up any that we're
michael@0 1735 // currently starting with their start value so we don't start
michael@0 1736 // them again for descendants that inherit that value.
michael@0 1737 nsCOMPtr<nsIStyleRule> coverRule =
michael@0 1738 aPresContext->TransitionManager()->StyleContextChanged(
michael@0 1739 aContent->AsElement(), aOldStyleContext, *aNewStyleContext);
michael@0 1740 if (coverRule) {
michael@0 1741 nsCOMArray<nsIStyleRule> rules;
michael@0 1742 rules.AppendObject(coverRule);
michael@0 1743 *aNewStyleContext = aPresContext->StyleSet()->
michael@0 1744 ResolveStyleByAddingRules(*aNewStyleContext, rules);
michael@0 1745 }
michael@0 1746 }
michael@0 1747
michael@0 1748 static inline dom::Element*
michael@0 1749 ElementForStyleContext(nsIContent* aParentContent,
michael@0 1750 nsIFrame* aFrame,
michael@0 1751 nsCSSPseudoElements::Type aPseudoType)
michael@0 1752 {
michael@0 1753 // We don't expect XUL tree stuff here.
michael@0 1754 NS_PRECONDITION(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
michael@0 1755 aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox ||
michael@0 1756 aPseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
michael@0 1757 "Unexpected pseudo");
michael@0 1758 // XXX see the comments about the various element confusion in
michael@0 1759 // ElementRestyler::Restyle.
michael@0 1760 if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
michael@0 1761 return aFrame->GetContent()->AsElement();
michael@0 1762 }
michael@0 1763
michael@0 1764 if (aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
michael@0 1765 return nullptr;
michael@0 1766 }
michael@0 1767
michael@0 1768 if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) {
michael@0 1769 NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame,
michael@0 1770 "firstLetter pseudoTag without a nsFirstLetterFrame");
michael@0 1771 nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
michael@0 1772 return block->GetContent()->AsElement();
michael@0 1773 }
michael@0 1774
michael@0 1775 if (aPseudoType == nsCSSPseudoElements::ePseudo_mozColorSwatch) {
michael@0 1776 MOZ_ASSERT(aFrame->GetParent() &&
michael@0 1777 aFrame->GetParent()->GetParent(),
michael@0 1778 "Color swatch frame should have a parent & grandparent");
michael@0 1779
michael@0 1780 nsIFrame* grandparentFrame = aFrame->GetParent()->GetParent();
michael@0 1781 MOZ_ASSERT(grandparentFrame->GetType() == nsGkAtoms::colorControlFrame,
michael@0 1782 "Color swatch's grandparent should be nsColorControlFrame");
michael@0 1783
michael@0 1784 return grandparentFrame->GetContent()->AsElement();
michael@0 1785 }
michael@0 1786
michael@0 1787 if (aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberText ||
michael@0 1788 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberWrapper ||
michael@0 1789 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinBox ||
michael@0 1790 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp ||
michael@0 1791 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown) {
michael@0 1792 // Get content for nearest nsNumberControlFrame:
michael@0 1793 nsIFrame* f = aFrame->GetParent();
michael@0 1794 MOZ_ASSERT(f);
michael@0 1795 while (f->GetType() != nsGkAtoms::numberControlFrame) {
michael@0 1796 f = f->GetParent();
michael@0 1797 MOZ_ASSERT(f);
michael@0 1798 }
michael@0 1799 return f->GetContent()->AsElement();
michael@0 1800 }
michael@0 1801
michael@0 1802 if (aParentContent) {
michael@0 1803 return aParentContent->AsElement();
michael@0 1804 }
michael@0 1805
michael@0 1806 MOZ_ASSERT(aFrame->GetContent()->GetParent(),
michael@0 1807 "should not have got here for the root element");
michael@0 1808 return aFrame->GetContent()->GetParent()->AsElement();
michael@0 1809 }
michael@0 1810
michael@0 1811 /**
michael@0 1812 * FIXME: Temporary. Should merge with following function.
michael@0 1813 */
michael@0 1814 static nsIFrame*
michael@0 1815 GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
michael@0 1816 {
michael@0 1817 // Account for {ib} splits when looking for "prevContinuation". In
michael@0 1818 // particular, for the first-continuation of a part of an {ib} split
michael@0 1819 // we want to use the previous ib-split sibling of the previous
michael@0 1820 // ib-split sibling of aFrame, which should have the same style
michael@0 1821 // context as aFrame itself. In particular, if aFrame is the first
michael@0 1822 // continuation of an inline part of a block-in-inline split then its
michael@0 1823 // previous ib-split sibling is a block, and the previous ib-split
michael@0 1824 // sibling of _that_ is an inline, just like aFrame. Similarly, if
michael@0 1825 // aFrame is the first continuation of a block part of an
michael@0 1826 // block-in-inline split (a block-in-inline wrapper block), then its
michael@0 1827 // previous ib-split sibling is an inline and the previous ib-split
michael@0 1828 // sibling of that is either another block-in-inline wrapper block box
michael@0 1829 // or null.
michael@0 1830 nsIFrame *prevContinuation = aFrame->GetPrevContinuation();
michael@0 1831 if (!prevContinuation &&
michael@0 1832 (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
michael@0 1833 // We're the first continuation, so we can just get the frame
michael@0 1834 // property directly
michael@0 1835 prevContinuation = static_cast<nsIFrame*>(
michael@0 1836 aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling()));
michael@0 1837 if (prevContinuation) {
michael@0 1838 prevContinuation = static_cast<nsIFrame*>(
michael@0 1839 prevContinuation->Properties().Get(nsIFrame::IBSplitPrevSibling()));
michael@0 1840 }
michael@0 1841 }
michael@0 1842
michael@0 1843 NS_ASSERTION(!prevContinuation ||
michael@0 1844 prevContinuation->GetContent() == aFrame->GetContent(),
michael@0 1845 "unexpected content mismatch");
michael@0 1846
michael@0 1847 return prevContinuation;
michael@0 1848 }
michael@0 1849
michael@0 1850 /**
michael@0 1851 * Get the previous continuation or similar ib-split sibling (assuming
michael@0 1852 * block/inline alternation), conditionally on it having the same style.
michael@0 1853 * This assumes that we're not between resolving the two (i.e., that
michael@0 1854 * they're both already resolved.
michael@0 1855 */
michael@0 1856 static nsIFrame*
michael@0 1857 GetPrevContinuationWithSameStyle(nsIFrame* aFrame)
michael@0 1858 {
michael@0 1859 nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
michael@0 1860 if (!prevContinuation) {
michael@0 1861 return nullptr;
michael@0 1862 }
michael@0 1863
michael@0 1864 nsStyleContext* prevStyle = prevContinuation->StyleContext();
michael@0 1865 nsStyleContext* selfStyle = aFrame->StyleContext();
michael@0 1866 if (prevStyle != selfStyle) {
michael@0 1867 NS_ASSERTION(prevStyle->GetPseudo() != selfStyle->GetPseudo() ||
michael@0 1868 prevStyle->GetParent() != selfStyle->GetParent(),
michael@0 1869 "continuations should have the same style context");
michael@0 1870 prevContinuation = nullptr;
michael@0 1871 }
michael@0 1872 return prevContinuation;
michael@0 1873 }
michael@0 1874
michael@0 1875 /**
michael@0 1876 * Get the next continuation or similar ib-split sibling (assuming
michael@0 1877 * block/inline alternation), conditionally on it having the same style.
michael@0 1878 *
michael@0 1879 * Since this is used when deciding to copy the new style context, it
michael@0 1880 * takes as an argument the old style context to check if the style is
michael@0 1881 * the same. When it is used in other contexts (i.e., where the next
michael@0 1882 * continuation would already have the new style context), the current
michael@0 1883 * style context should be passed.
michael@0 1884 */
michael@0 1885 static nsIFrame*
michael@0 1886 GetNextContinuationWithSameStyle(nsIFrame* aFrame,
michael@0 1887 nsStyleContext* aOldStyleContext)
michael@0 1888 {
michael@0 1889 // See GetPrevContinuationWithSameStyle about {ib} splits.
michael@0 1890
michael@0 1891 nsIFrame *nextContinuation = aFrame->GetNextContinuation();
michael@0 1892 if (!nextContinuation &&
michael@0 1893 (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
michael@0 1894 // We're the last continuation, so we have to hop back to the first
michael@0 1895 // before getting the frame property
michael@0 1896 nextContinuation = static_cast<nsIFrame*>(aFrame->FirstContinuation()->
michael@0 1897 Properties().Get(nsIFrame::IBSplitSibling()));
michael@0 1898 if (nextContinuation) {
michael@0 1899 nextContinuation = static_cast<nsIFrame*>(
michael@0 1900 nextContinuation->Properties().Get(nsIFrame::IBSplitSibling()));
michael@0 1901 }
michael@0 1902 }
michael@0 1903
michael@0 1904 if (!nextContinuation) {
michael@0 1905 return nullptr;
michael@0 1906 }
michael@0 1907
michael@0 1908 NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
michael@0 1909 "unexpected content mismatch");
michael@0 1910
michael@0 1911 nsStyleContext* nextStyle = nextContinuation->StyleContext();
michael@0 1912 if (nextStyle != aOldStyleContext) {
michael@0 1913 NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
michael@0 1914 aOldStyleContext->GetParent() != nextStyle->GetParent(),
michael@0 1915 "continuations should have the same style context");
michael@0 1916 nextContinuation = nullptr;
michael@0 1917 }
michael@0 1918 return nextContinuation;
michael@0 1919 }
michael@0 1920
michael@0 1921 nsresult
michael@0 1922 RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
michael@0 1923 {
michael@0 1924 if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
michael@0 1925 // Also reparent the out-of-flow and all its continuations.
michael@0 1926 nsIFrame* outOfFlow =
michael@0 1927 nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
michael@0 1928 NS_ASSERTION(outOfFlow, "no out-of-flow frame");
michael@0 1929 do {
michael@0 1930 ReparentStyleContext(outOfFlow);
michael@0 1931 } while ((outOfFlow = outOfFlow->GetNextContinuation()));
michael@0 1932 }
michael@0 1933
michael@0 1934 // DO NOT verify the style tree before reparenting. The frame
michael@0 1935 // tree has already been changed, so this check would just fail.
michael@0 1936 nsStyleContext* oldContext = aFrame->StyleContext();
michael@0 1937
michael@0 1938 nsRefPtr<nsStyleContext> newContext;
michael@0 1939 nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
michael@0 1940 bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
michael@0 1941 nsStyleContext* newParentContext = nullptr;
michael@0 1942 nsIFrame* providerChild = nullptr;
michael@0 1943 if (isChild) {
michael@0 1944 ReparentStyleContext(providerFrame);
michael@0 1945 newParentContext = providerFrame->StyleContext();
michael@0 1946 providerChild = providerFrame;
michael@0 1947 } else if (providerFrame) {
michael@0 1948 newParentContext = providerFrame->StyleContext();
michael@0 1949 } else {
michael@0 1950 NS_NOTREACHED("Reparenting something that has no usable parent? "
michael@0 1951 "Shouldn't happen!");
michael@0 1952 }
michael@0 1953 // XXX need to do something here to produce the correct style context for
michael@0 1954 // an IB split whose first inline part is inside a first-line frame.
michael@0 1955 // Currently the first IB anonymous block's style context takes the first
michael@0 1956 // part's style context as parent, which is wrong since first-line style
michael@0 1957 // should not apply to the anonymous block.
michael@0 1958
michael@0 1959 #ifdef DEBUG
michael@0 1960 {
michael@0 1961 // Check that our assumption that continuations of the same
michael@0 1962 // pseudo-type and with the same style context parent have the
michael@0 1963 // same style context is valid before the reresolution. (We need
michael@0 1964 // to check the pseudo-type and style context parent because of
michael@0 1965 // :first-letter and :first-line, where we create styled and
michael@0 1966 // unstyled letter/line frames distinguished by pseudo-type, and
michael@0 1967 // then need to distinguish their descendants based on having
michael@0 1968 // different parents.)
michael@0 1969 nsIFrame *nextContinuation = aFrame->GetNextContinuation();
michael@0 1970 if (nextContinuation) {
michael@0 1971 nsStyleContext *nextContinuationContext =
michael@0 1972 nextContinuation->StyleContext();
michael@0 1973 NS_ASSERTION(oldContext == nextContinuationContext ||
michael@0 1974 oldContext->GetPseudo() !=
michael@0 1975 nextContinuationContext->GetPseudo() ||
michael@0 1976 oldContext->GetParent() !=
michael@0 1977 nextContinuationContext->GetParent(),
michael@0 1978 "continuations should have the same style context");
michael@0 1979 }
michael@0 1980 }
michael@0 1981 #endif
michael@0 1982
michael@0 1983 nsIFrame *prevContinuation =
michael@0 1984 GetPrevContinuationWithPossiblySameStyle(aFrame);
michael@0 1985 nsStyleContext *prevContinuationContext;
michael@0 1986 bool copyFromContinuation =
michael@0 1987 prevContinuation &&
michael@0 1988 (prevContinuationContext = prevContinuation->StyleContext())
michael@0 1989 ->GetPseudo() == oldContext->GetPseudo() &&
michael@0 1990 prevContinuationContext->GetParent() == newParentContext;
michael@0 1991 if (copyFromContinuation) {
michael@0 1992 // Just use the style context from the frame's previous
michael@0 1993 // continuation (see assertion about aFrame->GetNextContinuation()
michael@0 1994 // above, which we would have previously hit for aFrame's previous
michael@0 1995 // continuation).
michael@0 1996 newContext = prevContinuationContext;
michael@0 1997 } else {
michael@0 1998 nsIFrame* parentFrame = aFrame->GetParent();
michael@0 1999 Element* element =
michael@0 2000 ElementForStyleContext(parentFrame ? parentFrame->GetContent() : nullptr,
michael@0 2001 aFrame,
michael@0 2002 oldContext->GetPseudoType());
michael@0 2003 newContext = mPresContext->StyleSet()->
michael@0 2004 ReparentStyleContext(oldContext, newParentContext, element);
michael@0 2005 }
michael@0 2006
michael@0 2007 if (newContext) {
michael@0 2008 if (newContext != oldContext) {
michael@0 2009 // We probably don't want to initiate transitions from
michael@0 2010 // ReparentStyleContext, since we call it during frame
michael@0 2011 // construction rather than in response to dynamic changes.
michael@0 2012 // Also see the comment at the start of
michael@0 2013 // nsTransitionManager::ConsiderStartingTransition.
michael@0 2014 #if 0
michael@0 2015 if (!copyFromContinuation) {
michael@0 2016 TryStartingTransition(mPresContext, aFrame->GetContent(),
michael@0 2017 oldContext, &newContext);
michael@0 2018 }
michael@0 2019 #endif
michael@0 2020
michael@0 2021 // Make sure to call CalcStyleDifference so that the new context ends
michael@0 2022 // up resolving all the structs the old context resolved.
michael@0 2023 if (!copyFromContinuation) {
michael@0 2024 DebugOnly<nsChangeHint> styleChange =
michael@0 2025 oldContext->CalcStyleDifference(newContext, nsChangeHint(0));
michael@0 2026 // The style change is always 0 because we have the same rulenode and
michael@0 2027 // CalcStyleDifference optimizes us away. That's OK, though:
michael@0 2028 // reparenting should never trigger a frame reconstruct, and whenever
michael@0 2029 // it's happening we already plan to reflow and repaint the frames.
michael@0 2030 NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
michael@0 2031 "Our frame tree is likely to be bogus!");
michael@0 2032 }
michael@0 2033
michael@0 2034 aFrame->SetStyleContext(newContext);
michael@0 2035
michael@0 2036 nsIFrame::ChildListIterator lists(aFrame);
michael@0 2037 for (; !lists.IsDone(); lists.Next()) {
michael@0 2038 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 2039 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 2040 nsIFrame* child = childFrames.get();
michael@0 2041 // only do frames that are in flow
michael@0 2042 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
michael@0 2043 child != providerChild) {
michael@0 2044 #ifdef DEBUG
michael@0 2045 if (nsGkAtoms::placeholderFrame == child->GetType()) {
michael@0 2046 nsIFrame* outOfFlowFrame =
michael@0 2047 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
michael@0 2048 NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
michael@0 2049
michael@0 2050 NS_ASSERTION(outOfFlowFrame != providerChild,
michael@0 2051 "Out of flow provider?");
michael@0 2052 }
michael@0 2053 #endif
michael@0 2054 ReparentStyleContext(child);
michael@0 2055 }
michael@0 2056 }
michael@0 2057 }
michael@0 2058
michael@0 2059 // If this frame is part of an IB split, then the style context of
michael@0 2060 // the next part of the split might be a child of our style context.
michael@0 2061 // Reparent its style context just in case one of our ancestors
michael@0 2062 // (split or not) hasn't done so already). It's not a problem to
michael@0 2063 // reparent the same frame twice because the "if (newContext !=
michael@0 2064 // oldContext)" check will prevent us from redoing work.
michael@0 2065 if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
michael@0 2066 !aFrame->GetPrevContinuation()) {
michael@0 2067 nsIFrame* sib = static_cast<nsIFrame*>
michael@0 2068 (aFrame->Properties().Get(nsIFrame::IBSplitSibling()));
michael@0 2069 if (sib) {
michael@0 2070 ReparentStyleContext(sib);
michael@0 2071 }
michael@0 2072 }
michael@0 2073
michael@0 2074 // do additional contexts
michael@0 2075 int32_t contextIndex = 0;
michael@0 2076 for (nsStyleContext* oldExtraContext;
michael@0 2077 (oldExtraContext = aFrame->GetAdditionalStyleContext(contextIndex));
michael@0 2078 ++contextIndex) {
michael@0 2079 nsRefPtr<nsStyleContext> newExtraContext;
michael@0 2080 newExtraContext = mPresContext->StyleSet()->
michael@0 2081 ReparentStyleContext(oldExtraContext,
michael@0 2082 newContext, nullptr);
michael@0 2083 if (newExtraContext) {
michael@0 2084 if (newExtraContext != oldExtraContext) {
michael@0 2085 // Make sure to call CalcStyleDifference so that the new
michael@0 2086 // context ends up resolving all the structs the old context
michael@0 2087 // resolved.
michael@0 2088 DebugOnly<nsChangeHint> styleChange =
michael@0 2089 oldExtraContext->CalcStyleDifference(newExtraContext,
michael@0 2090 nsChangeHint(0));
michael@0 2091 // The style change is always 0 because we have the same
michael@0 2092 // rulenode and CalcStyleDifference optimizes us away. That's
michael@0 2093 // OK, though: reparenting should never trigger a frame
michael@0 2094 // reconstruct, and whenever it's happening we already plan to
michael@0 2095 // reflow and repaint the frames.
michael@0 2096 NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
michael@0 2097 "Our frame tree is likely to be bogus!");
michael@0 2098 }
michael@0 2099
michael@0 2100 aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
michael@0 2101 }
michael@0 2102 }
michael@0 2103 #ifdef DEBUG
michael@0 2104 VerifyStyleTree(mPresContext, aFrame, newParentContext);
michael@0 2105 #endif
michael@0 2106 }
michael@0 2107 }
michael@0 2108
michael@0 2109 return NS_OK;
michael@0 2110 }
michael@0 2111
michael@0 2112 ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
michael@0 2113 nsIFrame* aFrame,
michael@0 2114 nsStyleChangeList* aChangeList,
michael@0 2115 nsChangeHint aHintsHandledByAncestors,
michael@0 2116 RestyleTracker& aRestyleTracker,
michael@0 2117 TreeMatchContext& aTreeMatchContext,
michael@0 2118 nsTArray<nsIContent*>&
michael@0 2119 aVisibleKidsOfHiddenElement)
michael@0 2120 : mPresContext(aPresContext)
michael@0 2121 , mFrame(aFrame)
michael@0 2122 , mParentContent(nullptr)
michael@0 2123 // XXXldb Why does it make sense to use aParentContent? (See
michael@0 2124 // comment above assertion at start of ElementRestyler::Restyle.)
michael@0 2125 , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
michael@0 2126 , mChangeList(aChangeList)
michael@0 2127 , mHintsHandled(NS_SubtractHint(aHintsHandledByAncestors,
michael@0 2128 NS_HintsNotHandledForDescendantsIn(aHintsHandledByAncestors)))
michael@0 2129 , mParentFrameHintsNotHandledForDescendants(nsChangeHint(0))
michael@0 2130 , mHintsNotHandledForDescendants(nsChangeHint(0))
michael@0 2131 , mRestyleTracker(aRestyleTracker)
michael@0 2132 , mTreeMatchContext(aTreeMatchContext)
michael@0 2133 , mResolvedChild(nullptr)
michael@0 2134 #ifdef ACCESSIBILITY
michael@0 2135 , mDesiredA11yNotifications(eSendAllNotifications)
michael@0 2136 , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
michael@0 2137 , mOurA11yNotification(eDontNotify)
michael@0 2138 , mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
michael@0 2139 #endif
michael@0 2140 {
michael@0 2141 }
michael@0 2142
michael@0 2143 ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
michael@0 2144 nsIFrame* aFrame,
michael@0 2145 uint32_t aConstructorFlags)
michael@0 2146 : mPresContext(aParentRestyler.mPresContext)
michael@0 2147 , mFrame(aFrame)
michael@0 2148 , mParentContent(aParentRestyler.mContent)
michael@0 2149 // XXXldb Why does it make sense to use aParentContent? (See
michael@0 2150 // comment above assertion at start of ElementRestyler::Restyle.)
michael@0 2151 , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
michael@0 2152 , mChangeList(aParentRestyler.mChangeList)
michael@0 2153 , mHintsHandled(NS_SubtractHint(aParentRestyler.mHintsHandled,
michael@0 2154 NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled)))
michael@0 2155 , mParentFrameHintsNotHandledForDescendants(
michael@0 2156 aParentRestyler.mHintsNotHandledForDescendants)
michael@0 2157 , mHintsNotHandledForDescendants(nsChangeHint(0))
michael@0 2158 , mRestyleTracker(aParentRestyler.mRestyleTracker)
michael@0 2159 , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
michael@0 2160 , mResolvedChild(nullptr)
michael@0 2161 #ifdef ACCESSIBILITY
michael@0 2162 , mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications)
michael@0 2163 , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
michael@0 2164 , mOurA11yNotification(eDontNotify)
michael@0 2165 , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
michael@0 2166 #endif
michael@0 2167 {
michael@0 2168 if (aConstructorFlags & FOR_OUT_OF_FLOW_CHILD) {
michael@0 2169 // Note that the out-of-flow may not be a geometric descendant of
michael@0 2170 // the frame where we started the reresolve. Therefore, even if
michael@0 2171 // mHintsHandled already includes nsChangeHint_AllReflowHints we
michael@0 2172 // don't want to pass that on to the out-of-flow reresolve, since
michael@0 2173 // that can lead to the out-of-flow not getting reflowed when it
michael@0 2174 // should be (eg a reresolve starting at <body> that involves
michael@0 2175 // reflowing the <body> would miss reflowing fixed-pos nodes that
michael@0 2176 // also need reflow). In the cases when the out-of-flow _is_ a
michael@0 2177 // geometric descendant of a frame we already have a reflow hint
michael@0 2178 // for, reflow coalescing should keep us from doing the work twice.
michael@0 2179 mHintsHandled = NS_SubtractHint(mHintsHandled, nsChangeHint_AllReflowHints);
michael@0 2180 }
michael@0 2181 }
michael@0 2182
michael@0 2183 ElementRestyler::ElementRestyler(ParentContextFromChildFrame,
michael@0 2184 const ElementRestyler& aParentRestyler,
michael@0 2185 nsIFrame* aFrame)
michael@0 2186 : mPresContext(aParentRestyler.mPresContext)
michael@0 2187 , mFrame(aFrame)
michael@0 2188 , mParentContent(aParentRestyler.mParentContent)
michael@0 2189 // XXXldb Why does it make sense to use aParentContent? (See
michael@0 2190 // comment above assertion at start of ElementRestyler::Restyle.)
michael@0 2191 , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
michael@0 2192 , mChangeList(aParentRestyler.mChangeList)
michael@0 2193 , mHintsHandled(NS_SubtractHint(aParentRestyler.mHintsHandled,
michael@0 2194 NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled)))
michael@0 2195 , mParentFrameHintsNotHandledForDescendants(
michael@0 2196 // assume the worst
michael@0 2197 nsChangeHint_Hints_NotHandledForDescendants)
michael@0 2198 , mHintsNotHandledForDescendants(nsChangeHint(0))
michael@0 2199 , mRestyleTracker(aParentRestyler.mRestyleTracker)
michael@0 2200 , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
michael@0 2201 , mResolvedChild(nullptr)
michael@0 2202 #ifdef ACCESSIBILITY
michael@0 2203 , mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications)
michael@0 2204 , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
michael@0 2205 , mOurA11yNotification(eDontNotify)
michael@0 2206 , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
michael@0 2207 #endif
michael@0 2208 {
michael@0 2209 }
michael@0 2210
michael@0 2211 void
michael@0 2212 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
michael@0 2213 nsStyleContext* aNewContext,
michael@0 2214 nsChangeHint aChangeToAssume)
michael@0 2215 {
michael@0 2216 // Check some invariants about replacing one style context with another.
michael@0 2217 NS_ASSERTION(aOldContext->GetPseudo() == aNewContext->GetPseudo(),
michael@0 2218 "old and new style contexts should have the same pseudo");
michael@0 2219 NS_ASSERTION(aOldContext->GetPseudoType() == aNewContext->GetPseudoType(),
michael@0 2220 "old and new style contexts should have the same pseudo");
michael@0 2221
michael@0 2222 nsChangeHint ourChange = aOldContext->CalcStyleDifference(aNewContext,
michael@0 2223 mParentFrameHintsNotHandledForDescendants);
michael@0 2224 NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) ||
michael@0 2225 (ourChange & nsChangeHint_NeedReflow),
michael@0 2226 "Reflow hint bits set without actually asking for a reflow");
michael@0 2227
michael@0 2228 // nsChangeHint_UpdateEffects is inherited, but it can be set due to changes
michael@0 2229 // in inherited properties (fill and stroke). Avoid propagating it into
michael@0 2230 // text nodes.
michael@0 2231 if ((ourChange & nsChangeHint_UpdateEffects) &&
michael@0 2232 mContent && !mContent->IsElement()) {
michael@0 2233 ourChange = NS_SubtractHint(ourChange, nsChangeHint_UpdateEffects);
michael@0 2234 }
michael@0 2235
michael@0 2236 NS_UpdateHint(ourChange, aChangeToAssume);
michael@0 2237 if (NS_UpdateHint(mHintsHandled, ourChange)) {
michael@0 2238 if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
michael@0 2239 mChangeList->AppendChange(mFrame, mContent, ourChange);
michael@0 2240 }
michael@0 2241 }
michael@0 2242 NS_UpdateHint(mHintsNotHandledForDescendants,
michael@0 2243 NS_HintsNotHandledForDescendantsIn(ourChange));
michael@0 2244 }
michael@0 2245
michael@0 2246 /**
michael@0 2247 * Recompute style for mFrame (which should not have a prev continuation
michael@0 2248 * with the same style), all of its next continuations with the same
michael@0 2249 * style, and all ib-split siblings of the same type (either block or
michael@0 2250 * inline, skipping the intermediates of the other type) and accumulate
michael@0 2251 * changes into mChangeList given that mHintsHandled is already accumulated
michael@0 2252 * for an ancestor.
michael@0 2253 * mParentContent is the content node used to resolve the parent style
michael@0 2254 * context. This means that, for pseudo-elements, it is the content
michael@0 2255 * that should be used for selector matching (rather than the fake
michael@0 2256 * content node attached to the frame).
michael@0 2257 */
michael@0 2258 void
michael@0 2259 ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
michael@0 2260 {
michael@0 2261 // It would be nice if we could make stronger assertions here; they
michael@0 2262 // would let us simplify the ?: expressions below setting |content|
michael@0 2263 // and |pseudoContent| in sensible ways as well as making what
michael@0 2264 // |content| and |pseudoContent| mean, and their relationship to
michael@0 2265 // |mFrame->GetContent()|, make more sense. However, we can't,
michael@0 2266 // because of frame trees like the one in
michael@0 2267 // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 . Once we
michael@0 2268 // fix bug 242277 we should be able to make this make more sense.
michael@0 2269 NS_ASSERTION(mFrame->GetContent() || !mParentContent ||
michael@0 2270 !mParentContent->GetParent(),
michael@0 2271 "frame must have content (unless at the top of the tree)");
michael@0 2272
michael@0 2273 NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
michael@0 2274 "should not be trying to restyle this frame separately");
michael@0 2275
michael@0 2276 if (mContent && mContent->IsElement()) {
michael@0 2277 mContent->OwnerDoc()->FlushPendingLinkUpdates();
michael@0 2278 RestyleTracker::RestyleData restyleData;
michael@0 2279 if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) {
michael@0 2280 if (NS_UpdateHint(mHintsHandled, restyleData.mChangeHint)) {
michael@0 2281 mChangeList->AppendChange(mFrame, mContent, restyleData.mChangeHint);
michael@0 2282 }
michael@0 2283 aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
michael@0 2284 }
michael@0 2285 }
michael@0 2286
michael@0 2287 nsRestyleHint childRestyleHint = aRestyleHint;
michael@0 2288
michael@0 2289 if (childRestyleHint == eRestyle_Self) {
michael@0 2290 childRestyleHint = nsRestyleHint(0);
michael@0 2291 }
michael@0 2292
michael@0 2293 {
michael@0 2294 nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
michael@0 2295
michael@0 2296 // TEMPORARY (until bug 918064): Call RestyleSelf for each
michael@0 2297 // continuation or block-in-inline sibling.
michael@0 2298
michael@0 2299 for (nsIFrame* f = mFrame; f;
michael@0 2300 f = GetNextContinuationWithSameStyle(f, oldContext)) {
michael@0 2301 RestyleSelf(f, aRestyleHint);
michael@0 2302 }
michael@0 2303 }
michael@0 2304
michael@0 2305 RestyleChildren(childRestyleHint);
michael@0 2306 }
michael@0 2307
michael@0 2308 void
michael@0 2309 ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
michael@0 2310 {
michael@0 2311 // XXXldb get new context from prev-in-flow if possible, to avoid
michael@0 2312 // duplication. (Or should we just let |GetContext| handle that?)
michael@0 2313 // Getting the hint would be nice too, but that's harder.
michael@0 2314
michael@0 2315 // XXXbryner we may be able to avoid some of the refcounting goop here.
michael@0 2316 // We do need a reference to oldContext for the lifetime of this function, and it's possible
michael@0 2317 // that the frame has the last reference to it, so AddRef it here.
michael@0 2318
michael@0 2319 nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
michael@0 2320 nsRefPtr<nsStyleContext> oldContext = aSelf->StyleContext();
michael@0 2321 nsStyleSet* styleSet = mPresContext->StyleSet();
michael@0 2322
michael@0 2323 #ifdef ACCESSIBILITY
michael@0 2324 mWasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
michael@0 2325 oldContext->StyleVisibility()->IsVisible() : false;
michael@0 2326 #endif
michael@0 2327
michael@0 2328 nsIAtom* const pseudoTag = oldContext->GetPseudo();
michael@0 2329 const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
michael@0 2330
michael@0 2331 nsStyleContext* parentContext;
michael@0 2332 // Get the frame providing the parent style context. If it is a
michael@0 2333 // child, then resolve the provider first.
michael@0 2334 nsIFrame* providerFrame = aSelf->GetParentStyleContextFrame();
michael@0 2335 bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
michael@0 2336 if (!isChild) {
michael@0 2337 if (providerFrame)
michael@0 2338 parentContext = providerFrame->StyleContext();
michael@0 2339 else
michael@0 2340 parentContext = nullptr;
michael@0 2341 }
michael@0 2342 else {
michael@0 2343 MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
michael@0 2344 "Postcondition for GetParentStyleContextFrame() violated. "
michael@0 2345 "That means we need to add the current element to the "
michael@0 2346 "ancestor filter.");
michael@0 2347
michael@0 2348 // resolve the provider here (before aSelf below).
michael@0 2349
michael@0 2350 // assumeDifferenceHint forces the parent's change to be also
michael@0 2351 // applied to this frame, no matter what
michael@0 2352 // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
michael@0 2353 // can't be trusted because it assumes any changes to the parent
michael@0 2354 // style context provider will be automatically propagated to
michael@0 2355 // the frame(s) with child style contexts.
michael@0 2356
michael@0 2357 ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME,
michael@0 2358 *this, providerFrame);
michael@0 2359 providerRestyler.Restyle(aRestyleHint);
michael@0 2360 assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
michael@0 2361
michael@0 2362 // The provider's new context becomes the parent context of
michael@0 2363 // aSelf's context.
michael@0 2364 parentContext = providerFrame->StyleContext();
michael@0 2365 // Set |mResolvedChild| so we don't bother resolving the
michael@0 2366 // provider again.
michael@0 2367 mResolvedChild = providerFrame;
michael@0 2368 }
michael@0 2369
michael@0 2370 if (providerFrame != aSelf->GetParent()) {
michael@0 2371 // We don't actually know what the parent style context's
michael@0 2372 // non-inherited hints were, so assume the worst.
michael@0 2373 mParentFrameHintsNotHandledForDescendants =
michael@0 2374 nsChangeHint_Hints_NotHandledForDescendants;
michael@0 2375 }
michael@0 2376
michael@0 2377 // do primary context
michael@0 2378 nsRefPtr<nsStyleContext> newContext;
michael@0 2379 nsIFrame *prevContinuation =
michael@0 2380 GetPrevContinuationWithPossiblySameStyle(aSelf);
michael@0 2381 nsStyleContext *prevContinuationContext;
michael@0 2382 bool copyFromContinuation =
michael@0 2383 prevContinuation &&
michael@0 2384 (prevContinuationContext = prevContinuation->StyleContext())
michael@0 2385 ->GetPseudo() == oldContext->GetPseudo() &&
michael@0 2386 prevContinuationContext->GetParent() == parentContext;
michael@0 2387 if (copyFromContinuation) {
michael@0 2388 // Just use the style context from the frame's previous
michael@0 2389 // continuation.
michael@0 2390 newContext = prevContinuationContext;
michael@0 2391 }
michael@0 2392 else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
michael@0 2393 NS_ASSERTION(aSelf->GetContent(),
michael@0 2394 "non pseudo-element frame without content node");
michael@0 2395 newContext = styleSet->ResolveStyleForNonElement(parentContext);
michael@0 2396 }
michael@0 2397 else if (!aRestyleHint && !prevContinuation) {
michael@0 2398 // Unfortunately, if prevContinuation is non-null then we may have
michael@0 2399 // already stolen the restyle tracker entry for this element while
michael@0 2400 // processing prevContinuation. So we don't know whether aRestyleHint
michael@0 2401 // should really be 0 here or whether it should be eRestyle_Self. Be
michael@0 2402 // pessimistic and force an actual reresolve in that situation. The good
michael@0 2403 // news is that in the common case when prevContinuation is non-null we
michael@0 2404 // just used prevContinuationContext anyway and aren't reaching this code
michael@0 2405 // to start with.
michael@0 2406 newContext =
michael@0 2407 styleSet->ReparentStyleContext(oldContext, parentContext,
michael@0 2408 ElementForStyleContext(mParentContent,
michael@0 2409 aSelf, pseudoType));
michael@0 2410 } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
michael@0 2411 newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
michael@0 2412 parentContext);
michael@0 2413 }
michael@0 2414 else {
michael@0 2415 Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
michael@0 2416 if (pseudoTag) {
michael@0 2417 if (pseudoTag == nsCSSPseudoElements::before ||
michael@0 2418 pseudoTag == nsCSSPseudoElements::after) {
michael@0 2419 // XXX what other pseudos do we need to treat like this?
michael@0 2420 newContext = styleSet->ProbePseudoElementStyle(element,
michael@0 2421 pseudoType,
michael@0 2422 parentContext,
michael@0 2423 mTreeMatchContext);
michael@0 2424 if (!newContext) {
michael@0 2425 // This pseudo should no longer exist; gotta reframe
michael@0 2426 NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
michael@0 2427 mChangeList->AppendChange(aSelf, element,
michael@0 2428 nsChangeHint_ReconstructFrame);
michael@0 2429 // We're reframing anyway; just keep the same context
michael@0 2430 newContext = oldContext;
michael@0 2431 }
michael@0 2432 } else {
michael@0 2433 // Don't expect XUL tree stuff here, since it needs a comparator and
michael@0 2434 // all.
michael@0 2435 NS_ASSERTION(pseudoType <
michael@0 2436 nsCSSPseudoElements::ePseudo_PseudoElementCount,
michael@0 2437 "Unexpected pseudo type");
michael@0 2438 Element* pseudoElement =
michael@0 2439 nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(pseudoType) ||
michael@0 2440 nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType) ?
michael@0 2441 aSelf->GetContent()->AsElement() : nullptr;
michael@0 2442 MOZ_ASSERT(element != pseudoElement);
michael@0 2443 newContext = styleSet->ResolvePseudoElementStyle(element,
michael@0 2444 pseudoType,
michael@0 2445 parentContext,
michael@0 2446 pseudoElement);
michael@0 2447 }
michael@0 2448 }
michael@0 2449 else {
michael@0 2450 NS_ASSERTION(aSelf->GetContent(),
michael@0 2451 "non pseudo-element frame without content node");
michael@0 2452 // Skip flex-item style fixup for anonymous subtrees:
michael@0 2453 TreeMatchContext::AutoFlexItemStyleFixupSkipper
michael@0 2454 flexFixupSkipper(mTreeMatchContext,
michael@0 2455 element->IsRootOfNativeAnonymousSubtree());
michael@0 2456 newContext = styleSet->ResolveStyleFor(element, parentContext,
michael@0 2457 mTreeMatchContext);
michael@0 2458 }
michael@0 2459 }
michael@0 2460
michael@0 2461 MOZ_ASSERT(newContext);
michael@0 2462
michael@0 2463 if (!parentContext) {
michael@0 2464 if (oldContext->RuleNode() == newContext->RuleNode() &&
michael@0 2465 oldContext->IsLinkContext() == newContext->IsLinkContext() &&
michael@0 2466 oldContext->RelevantLinkVisited() ==
michael@0 2467 newContext->RelevantLinkVisited()) {
michael@0 2468 // We're the root of the style context tree and the new style
michael@0 2469 // context returned has the same rule node. This means that
michael@0 2470 // we can use FindChildWithRules to keep a lot of the old
michael@0 2471 // style contexts around. However, we need to start from the
michael@0 2472 // same root.
michael@0 2473 newContext = oldContext;
michael@0 2474 }
michael@0 2475 }
michael@0 2476
michael@0 2477 if (newContext != oldContext) {
michael@0 2478 if (!copyFromContinuation) {
michael@0 2479 TryStartingTransition(mPresContext, aSelf->GetContent(),
michael@0 2480 oldContext, &newContext);
michael@0 2481
michael@0 2482 CaptureChange(oldContext, newContext, assumeDifferenceHint);
michael@0 2483 }
michael@0 2484
michael@0 2485 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
michael@0 2486 // If the frame gets regenerated, let it keep its old context,
michael@0 2487 // which is important to maintain various invariants about
michael@0 2488 // frame types matching their style contexts.
michael@0 2489 // Note that this check even makes sense if we didn't call
michael@0 2490 // CaptureChange because of copyFromContinuation being true,
michael@0 2491 // since we'll have copied the existing context from the
michael@0 2492 // previous continuation, so newContext == oldContext.
michael@0 2493 aSelf->SetStyleContext(newContext);
michael@0 2494 }
michael@0 2495 }
michael@0 2496 oldContext = nullptr;
michael@0 2497
michael@0 2498 // do additional contexts
michael@0 2499 // XXXbz might be able to avoid selector matching here in some
michael@0 2500 // cases; won't worry about it for now.
michael@0 2501 int32_t contextIndex = 0;
michael@0 2502 for (nsStyleContext* oldExtraContext;
michael@0 2503 (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
michael@0 2504 ++contextIndex) {
michael@0 2505 nsRefPtr<nsStyleContext> newExtraContext;
michael@0 2506 nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
michael@0 2507 const nsCSSPseudoElements::Type extraPseudoType =
michael@0 2508 oldExtraContext->GetPseudoType();
michael@0 2509 NS_ASSERTION(extraPseudoTag &&
michael@0 2510 extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
michael@0 2511 "extra style context is not pseudo element");
michael@0 2512 if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
michael@0 2513 newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
michael@0 2514 newContext);
michael@0 2515 }
michael@0 2516 else {
michael@0 2517 // Don't expect XUL tree stuff here, since it needs a comparator and
michael@0 2518 // all.
michael@0 2519 NS_ASSERTION(extraPseudoType <
michael@0 2520 nsCSSPseudoElements::ePseudo_PseudoElementCount,
michael@0 2521 "Unexpected type");
michael@0 2522 newExtraContext = styleSet->ResolvePseudoElementStyle(mContent->AsElement(),
michael@0 2523 extraPseudoType,
michael@0 2524 newContext,
michael@0 2525 nullptr);
michael@0 2526 }
michael@0 2527
michael@0 2528 MOZ_ASSERT(newExtraContext);
michael@0 2529
michael@0 2530 if (oldExtraContext != newExtraContext) {
michael@0 2531 CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint);
michael@0 2532 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
michael@0 2533 aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
michael@0 2534 }
michael@0 2535 }
michael@0 2536 }
michael@0 2537 }
michael@0 2538
michael@0 2539 void
michael@0 2540 ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
michael@0 2541 {
michael@0 2542 RestyleUndisplayedChildren(aChildRestyleHint);
michael@0 2543
michael@0 2544 // Check whether we might need to create a new ::before frame.
michael@0 2545 // There's no need to do this if we're planning to reframe already
michael@0 2546 // or if we're not forcing restyles on kids.
michael@0 2547 // It's also important to check mHintsHandled since we use
michael@0 2548 // mFrame->StyleContext(), which is out of date if mHintsHandled has a
michael@0 2549 // ReconstructFrame hint. Using an out of date style context could
michael@0 2550 // trigger assertions about mismatched rule trees.
michael@0 2551 if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
michael@0 2552 aChildRestyleHint) {
michael@0 2553 RestyleBeforePseudo();
michael@0 2554 }
michael@0 2555
michael@0 2556 // There is no need to waste time crawling into a frame's children
michael@0 2557 // on a frame change. The act of reconstructing frames will force
michael@0 2558 // new style contexts to be resolved on all of this frame's
michael@0 2559 // descendants anyway, so we want to avoid wasting time processing
michael@0 2560 // style contexts that we're just going to throw away anyway. - dwh
michael@0 2561 // It's also important to check mHintsHandled since reresolving the
michael@0 2562 // kids would use mFrame->StyleContext(), which is out of date if
michael@0 2563 // mHintsHandled has a ReconstructFrame hint; doing this could trigger
michael@0 2564 // assertions about mismatched rule trees.
michael@0 2565 nsIFrame *lastContinuation;
michael@0 2566 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
michael@0 2567 InitializeAccessibilityNotifications();
michael@0 2568
michael@0 2569 for (nsIFrame* f = mFrame; f;
michael@0 2570 f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
michael@0 2571 lastContinuation = f;
michael@0 2572 RestyleContentChildren(f, aChildRestyleHint);
michael@0 2573 }
michael@0 2574
michael@0 2575 SendAccessibilityNotifications();
michael@0 2576 }
michael@0 2577
michael@0 2578 // Check whether we might need to create a new ::after frame.
michael@0 2579 // See comments above regarding :before.
michael@0 2580 if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
michael@0 2581 aChildRestyleHint) {
michael@0 2582 RestyleAfterPseudo(lastContinuation);
michael@0 2583 }
michael@0 2584 }
michael@0 2585
michael@0 2586 void
michael@0 2587 ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
michael@0 2588 {
michael@0 2589 // When the root element is display:none, we still construct *some*
michael@0 2590 // frames that have the root element as their mContent, down to the
michael@0 2591 // DocElementContainingBlock.
michael@0 2592 bool checkUndisplayed;
michael@0 2593 nsIContent* undisplayedParent;
michael@0 2594 nsCSSFrameConstructor* frameConstructor = mPresContext->FrameConstructor();
michael@0 2595 if (mFrame->StyleContext()->GetPseudo()) {
michael@0 2596 checkUndisplayed = mFrame == frameConstructor->
michael@0 2597 GetDocElementContainingBlock();
michael@0 2598 undisplayedParent = nullptr;
michael@0 2599 } else {
michael@0 2600 checkUndisplayed = !!mFrame->GetContent();
michael@0 2601 undisplayedParent = mFrame->GetContent();
michael@0 2602 }
michael@0 2603 if (checkUndisplayed &&
michael@0 2604 // No need to do this if we're planning to reframe already.
michael@0 2605 // It's also important to check mHintsHandled since we use
michael@0 2606 // mFrame->StyleContext(), which is out of date if mHintsHandled
michael@0 2607 // has a ReconstructFrame hint. Using an out of date style
michael@0 2608 // context could trigger assertions about mismatched rule trees.
michael@0 2609 !(mHintsHandled & nsChangeHint_ReconstructFrame)) {
michael@0 2610 UndisplayedNode* undisplayed =
michael@0 2611 frameConstructor->GetAllUndisplayedContentIn(undisplayedParent);
michael@0 2612 TreeMatchContext::AutoAncestorPusher pusher(mTreeMatchContext);
michael@0 2613 if (undisplayed) {
michael@0 2614 pusher.PushAncestorAndStyleScope(undisplayedParent);
michael@0 2615 }
michael@0 2616 for (; undisplayed; undisplayed = undisplayed->mNext) {
michael@0 2617 NS_ASSERTION(undisplayedParent ||
michael@0 2618 undisplayed->mContent ==
michael@0 2619 mPresContext->Document()->GetRootElement(),
michael@0 2620 "undisplayed node child of null must be root");
michael@0 2621 NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
michael@0 2622 "Shouldn't have random pseudo style contexts in the "
michael@0 2623 "undisplayed map");
michael@0 2624
michael@0 2625 // Get the parent of the undisplayed content and check if it is a XBL
michael@0 2626 // children element. Push the children element as an ancestor here because it does
michael@0 2627 // not have a frame and would not otherwise be pushed as an ancestor.
michael@0 2628 nsIContent* parent = undisplayed->mContent->GetParent();
michael@0 2629 TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
michael@0 2630 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
michael@0 2631 insertionPointPusher.PushAncestorAndStyleScope(parent);
michael@0 2632 }
michael@0 2633
michael@0 2634 nsRestyleHint thisChildHint = aChildRestyleHint;
michael@0 2635 RestyleTracker::RestyleData undisplayedRestyleData;
michael@0 2636 if (mRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
michael@0 2637 &undisplayedRestyleData)) {
michael@0 2638 thisChildHint =
michael@0 2639 nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
michael@0 2640 }
michael@0 2641 nsRefPtr<nsStyleContext> undisplayedContext;
michael@0 2642 nsStyleSet* styleSet = mPresContext->StyleSet();
michael@0 2643 if (thisChildHint) {
michael@0 2644 undisplayedContext =
michael@0 2645 styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(),
michael@0 2646 mFrame->StyleContext(),
michael@0 2647 mTreeMatchContext);
michael@0 2648 } else {
michael@0 2649 undisplayedContext =
michael@0 2650 styleSet->ReparentStyleContext(undisplayed->mStyle,
michael@0 2651 mFrame->StyleContext(),
michael@0 2652 undisplayed->mContent->AsElement());
michael@0 2653 }
michael@0 2654 const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
michael@0 2655 if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
michael@0 2656 NS_ASSERTION(undisplayed->mContent,
michael@0 2657 "Must have undisplayed content");
michael@0 2658 mChangeList->AppendChange(nullptr, undisplayed->mContent,
michael@0 2659 NS_STYLE_HINT_FRAMECHANGE);
michael@0 2660 // The node should be removed from the undisplayed map when
michael@0 2661 // we reframe it.
michael@0 2662 } else {
michael@0 2663 // update the undisplayed node with the new context
michael@0 2664 undisplayed->mStyle = undisplayedContext;
michael@0 2665 }
michael@0 2666 }
michael@0 2667 }
michael@0 2668 }
michael@0 2669
michael@0 2670 void
michael@0 2671 ElementRestyler::RestyleBeforePseudo()
michael@0 2672 {
michael@0 2673 // Make sure not to do this for pseudo-frames or frames that
michael@0 2674 // can't have generated content.
michael@0 2675 if (!mFrame->StyleContext()->GetPseudo() &&
michael@0 2676 ((mFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
michael@0 2677 // Our content insertion frame might have gotten flagged
michael@0 2678 (mFrame->GetContentInsertionFrame()->GetStateBits() &
michael@0 2679 NS_FRAME_MAY_HAVE_GENERATED_CONTENT))) {
michael@0 2680 // Check for a new :before pseudo and an existing :before
michael@0 2681 // frame, but only if the frame is the first continuation.
michael@0 2682 nsIFrame* prevContinuation = mFrame->GetPrevContinuation();
michael@0 2683 if (!prevContinuation) {
michael@0 2684 // Checking for a :before frame is cheaper than getting the
michael@0 2685 // :before style context.
michael@0 2686 if (!nsLayoutUtils::GetBeforeFrame(mFrame) &&
michael@0 2687 nsLayoutUtils::HasPseudoStyle(mFrame->GetContent(),
michael@0 2688 mFrame->StyleContext(),
michael@0 2689 nsCSSPseudoElements::ePseudo_before,
michael@0 2690 mPresContext)) {
michael@0 2691 // Have to create the new :before frame
michael@0 2692 NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
michael@0 2693 mChangeList->AppendChange(mFrame, mContent,
michael@0 2694 nsChangeHint_ReconstructFrame);
michael@0 2695 }
michael@0 2696 }
michael@0 2697 }
michael@0 2698 }
michael@0 2699
michael@0 2700 /**
michael@0 2701 * aFrame is the last continuation or block-in-inline sibling that this
michael@0 2702 * ElementRestyler is restyling.
michael@0 2703 */
michael@0 2704 void
michael@0 2705 ElementRestyler::RestyleAfterPseudo(nsIFrame* aFrame)
michael@0 2706 {
michael@0 2707 // Make sure not to do this for pseudo-frames or frames that
michael@0 2708 // can't have generated content.
michael@0 2709 if (!aFrame->StyleContext()->GetPseudo() &&
michael@0 2710 ((aFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
michael@0 2711 // Our content insertion frame might have gotten flagged
michael@0 2712 (aFrame->GetContentInsertionFrame()->GetStateBits() &
michael@0 2713 NS_FRAME_MAY_HAVE_GENERATED_CONTENT))) {
michael@0 2714 // Check for new :after content, but only if the frame is the
michael@0 2715 // last continuation.
michael@0 2716 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
michael@0 2717
michael@0 2718 if (!nextContinuation) {
michael@0 2719 // Getting the :after frame is more expensive than getting the pseudo
michael@0 2720 // context, so get the pseudo context first.
michael@0 2721 if (nsLayoutUtils::HasPseudoStyle(aFrame->GetContent(),
michael@0 2722 aFrame->StyleContext(),
michael@0 2723 nsCSSPseudoElements::ePseudo_after,
michael@0 2724 mPresContext) &&
michael@0 2725 !nsLayoutUtils::GetAfterFrame(aFrame)) {
michael@0 2726 // have to create the new :after frame
michael@0 2727 NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
michael@0 2728 mChangeList->AppendChange(aFrame, mContent,
michael@0 2729 nsChangeHint_ReconstructFrame);
michael@0 2730 }
michael@0 2731 }
michael@0 2732 }
michael@0 2733 }
michael@0 2734
michael@0 2735 void
michael@0 2736 ElementRestyler::InitializeAccessibilityNotifications()
michael@0 2737 {
michael@0 2738 #ifdef ACCESSIBILITY
michael@0 2739 // Notify a11y for primary frame only if it's a root frame of visibility
michael@0 2740 // changes or its parent frame was hidden while it stays visible and
michael@0 2741 // it is not inside a {ib} split or is the first frame of {ib} split.
michael@0 2742 if (nsIPresShell::IsAccessibilityActive() &&
michael@0 2743 !mFrame->GetPrevContinuation() &&
michael@0 2744 !mFrame->FrameIsNonFirstInIBSplit()) {
michael@0 2745 if (mDesiredA11yNotifications == eSendAllNotifications) {
michael@0 2746 bool isFrameVisible = mFrame->StyleVisibility()->IsVisible();
michael@0 2747 if (isFrameVisible != mWasFrameVisible) {
michael@0 2748 if (isFrameVisible) {
michael@0 2749 // Notify a11y the element (perhaps with its children) was shown.
michael@0 2750 // We don't fall into this case if this element gets or stays shown
michael@0 2751 // while its parent becomes hidden.
michael@0 2752 mKidsDesiredA11yNotifications = eSkipNotifications;
michael@0 2753 mOurA11yNotification = eNotifyShown;
michael@0 2754 } else {
michael@0 2755 // The element is being hidden; its children may stay visible, or
michael@0 2756 // become visible after being hidden previously. If we'll find
michael@0 2757 // visible children then we should notify a11y about that as if
michael@0 2758 // they were inserted into tree. Notify a11y this element was
michael@0 2759 // hidden.
michael@0 2760 mKidsDesiredA11yNotifications = eNotifyIfShown;
michael@0 2761 mOurA11yNotification = eNotifyHidden;
michael@0 2762 }
michael@0 2763 }
michael@0 2764 } else if (mDesiredA11yNotifications == eNotifyIfShown &&
michael@0 2765 mFrame->StyleVisibility()->IsVisible()) {
michael@0 2766 // Notify a11y that element stayed visible while its parent was
michael@0 2767 // hidden.
michael@0 2768 mVisibleKidsOfHiddenElement.AppendElement(mFrame->GetContent());
michael@0 2769 mKidsDesiredA11yNotifications = eSkipNotifications;
michael@0 2770 }
michael@0 2771 }
michael@0 2772 #endif
michael@0 2773 }
michael@0 2774
michael@0 2775 void
michael@0 2776 ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
michael@0 2777 nsRestyleHint aChildRestyleHint)
michael@0 2778 {
michael@0 2779 nsIFrame::ChildListIterator lists(aParent);
michael@0 2780 TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
michael@0 2781 if (!lists.IsDone()) {
michael@0 2782 ancestorPusher.PushAncestorAndStyleScope(mContent);
michael@0 2783 }
michael@0 2784 for (; !lists.IsDone(); lists.Next()) {
michael@0 2785 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 2786 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 2787 nsIFrame* child = childFrames.get();
michael@0 2788 // Out-of-flows are reached through their placeholders. Continuations
michael@0 2789 // and block-in-inline splits are reached through those chains.
michael@0 2790 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
michael@0 2791 !GetPrevContinuationWithSameStyle(child)) {
michael@0 2792 // Get the parent of the child frame's content and check if it
michael@0 2793 // is a XBL children element. Push the children element as an
michael@0 2794 // ancestor here because it does not have a frame and would not
michael@0 2795 // otherwise be pushed as an ancestor.
michael@0 2796
michael@0 2797 // Check if the frame has a content because |child| may be a
michael@0 2798 // nsPageFrame that does not have a content.
michael@0 2799 nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr;
michael@0 2800 TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
michael@0 2801 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
michael@0 2802 insertionPointPusher.PushAncestorAndStyleScope(parent);
michael@0 2803 }
michael@0 2804
michael@0 2805 // only do frames that are in flow
michael@0 2806 if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder
michael@0 2807 // get out of flow frame and recur there
michael@0 2808 nsIFrame* outOfFlowFrame =
michael@0 2809 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
michael@0 2810 NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
michael@0 2811 NS_ASSERTION(outOfFlowFrame != mResolvedChild,
michael@0 2812 "out-of-flow frame not a true descendant");
michael@0 2813
michael@0 2814 // |nsFrame::GetParentStyleContextFrame| checks being out
michael@0 2815 // of flow so that this works correctly.
michael@0 2816 do {
michael@0 2817 if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
michael@0 2818 // Later continuations are likely restyled as a result of
michael@0 2819 // the restyling of the previous continuation.
michael@0 2820 // (Currently that's always true, but it's likely to
michael@0 2821 // change if we implement overflow:fragments or similar.)
michael@0 2822 continue;
michael@0 2823 }
michael@0 2824 ElementRestyler oofRestyler(*this, outOfFlowFrame,
michael@0 2825 FOR_OUT_OF_FLOW_CHILD);
michael@0 2826 oofRestyler.Restyle(aChildRestyleHint);
michael@0 2827 } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
michael@0 2828
michael@0 2829 // reresolve placeholder's context under the same parent
michael@0 2830 // as the out-of-flow frame
michael@0 2831 ElementRestyler phRestyler(*this, child, 0);
michael@0 2832 phRestyler.Restyle(aChildRestyleHint);
michael@0 2833 }
michael@0 2834 else { // regular child frame
michael@0 2835 if (child != mResolvedChild) {
michael@0 2836 ElementRestyler childRestyler(*this, child, 0);
michael@0 2837 childRestyler.Restyle(aChildRestyleHint);
michael@0 2838 }
michael@0 2839 }
michael@0 2840 }
michael@0 2841 }
michael@0 2842 }
michael@0 2843 // XXX need to do overflow frames???
michael@0 2844 }
michael@0 2845
michael@0 2846 void
michael@0 2847 ElementRestyler::SendAccessibilityNotifications()
michael@0 2848 {
michael@0 2849 #ifdef ACCESSIBILITY
michael@0 2850 // Send notifications about visibility changes.
michael@0 2851 if (mOurA11yNotification == eNotifyShown) {
michael@0 2852 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 2853 if (accService) {
michael@0 2854 nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
michael@0 2855 nsIContent* content = mFrame->GetContent();
michael@0 2856
michael@0 2857 accService->ContentRangeInserted(presShell, content->GetParent(),
michael@0 2858 content,
michael@0 2859 content->GetNextSibling());
michael@0 2860 }
michael@0 2861 } else if (mOurA11yNotification == eNotifyHidden) {
michael@0 2862 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 2863 if (accService) {
michael@0 2864 nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
michael@0 2865 nsIContent* content = mFrame->GetContent();
michael@0 2866 accService->ContentRemoved(presShell, content->GetParent(), content);
michael@0 2867
michael@0 2868 // Process children staying shown.
michael@0 2869 uint32_t visibleContentCount = mVisibleKidsOfHiddenElement.Length();
michael@0 2870 for (uint32_t idx = 0; idx < visibleContentCount; idx++) {
michael@0 2871 nsIContent* childContent = mVisibleKidsOfHiddenElement[idx];
michael@0 2872 accService->ContentRangeInserted(presShell, childContent->GetParent(),
michael@0 2873 childContent,
michael@0 2874 childContent->GetNextSibling());
michael@0 2875 }
michael@0 2876 mVisibleKidsOfHiddenElement.Clear();
michael@0 2877 }
michael@0 2878 }
michael@0 2879 #endif
michael@0 2880 }
michael@0 2881
michael@0 2882 static inline nsIFrame*
michael@0 2883 GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
michael@0 2884 {
michael@0 2885 NS_ASSERTION(!aFrame->GetPrevContinuation(),
michael@0 2886 "must start with the first continuation");
michael@0 2887 // Might we have ib-split siblings?
michael@0 2888 if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
michael@0 2889 // nothing more to do here
michael@0 2890 return nullptr;
michael@0 2891 }
michael@0 2892
michael@0 2893 return static_cast<nsIFrame*>
michael@0 2894 (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
michael@0 2895 }
michael@0 2896
michael@0 2897 void
michael@0 2898 RestyleManager::ComputeStyleChangeFor(nsIFrame* aFrame,
michael@0 2899 nsStyleChangeList* aChangeList,
michael@0 2900 nsChangeHint aMinChange,
michael@0 2901 RestyleTracker& aRestyleTracker,
michael@0 2902 bool aRestyleDescendants)
michael@0 2903 {
michael@0 2904 PROFILER_LABEL("CSS", "ComputeStyleChangeFor");
michael@0 2905
michael@0 2906 nsIContent *content = aFrame->GetContent();
michael@0 2907 if (aMinChange) {
michael@0 2908 aChangeList->AppendChange(aFrame, content, aMinChange);
michael@0 2909 }
michael@0 2910
michael@0 2911 NS_ASSERTION(!aFrame->GetPrevContinuation(),
michael@0 2912 "must start with the first continuation");
michael@0 2913
michael@0 2914 // We want to start with this frame and walk all its next-in-flows,
michael@0 2915 // as well as all its ib-split siblings and their next-in-flows,
michael@0 2916 // reresolving style on all the frames we encounter in this walk that
michael@0 2917 // we didn't reach already. In the normal case, this will mean only
michael@0 2918 // restyling the first two block-in-inline splits and no
michael@0 2919 // continuations, and skipping everything else. However, when we have
michael@0 2920 // a style change targeted at an element inside a context where styles
michael@0 2921 // vary between continuations (e.g., a style change on an element that
michael@0 2922 // extends from inside a styled ::first-line to outside of that first
michael@0 2923 // line), we might restyle more than that.
michael@0 2924
michael@0 2925 FramePropertyTable* propTable = mPresContext->PropertyTable();
michael@0 2926
michael@0 2927 TreeMatchContext treeMatchContext(true,
michael@0 2928 nsRuleWalker::eRelevantLinkUnvisited,
michael@0 2929 mPresContext->Document());
michael@0 2930 nsIContent *parent = content ? content->GetParent() : nullptr;
michael@0 2931 Element *parentElement =
michael@0 2932 parent && parent->IsElement() ? parent->AsElement() : nullptr;
michael@0 2933 treeMatchContext.InitAncestors(parentElement);
michael@0 2934 nsTArray<nsIContent*> visibleKidsOfHiddenElement;
michael@0 2935 for (nsIFrame* ibSibling = aFrame; ibSibling;
michael@0 2936 ibSibling = GetNextBlockInInlineSibling(propTable, ibSibling)) {
michael@0 2937 // Outer loop over ib-split siblings
michael@0 2938 for (nsIFrame* cont = ibSibling; cont; cont = cont->GetNextContinuation()) {
michael@0 2939 if (GetPrevContinuationWithSameStyle(cont)) {
michael@0 2940 // We already handled this element when dealing with its earlier
michael@0 2941 // continuation.
michael@0 2942 continue;
michael@0 2943 }
michael@0 2944
michael@0 2945 // Inner loop over next-in-flows of the current frame
michael@0 2946 ElementRestyler restyler(mPresContext, cont, aChangeList,
michael@0 2947 aMinChange, aRestyleTracker,
michael@0 2948 treeMatchContext,
michael@0 2949 visibleKidsOfHiddenElement);
michael@0 2950
michael@0 2951 restyler.Restyle(aRestyleDescendants ? eRestyle_Subtree : eRestyle_Self);
michael@0 2952
michael@0 2953 if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
michael@0 2954 // If it's going to cause a framechange, then don't bother
michael@0 2955 // with the continuations or ib-split siblings since they'll be
michael@0 2956 // clobbered by the frame reconstruct anyway.
michael@0 2957 NS_ASSERTION(!cont->GetPrevContinuation(),
michael@0 2958 "continuing frame had more severe impact than first-in-flow");
michael@0 2959 return;
michael@0 2960 }
michael@0 2961 }
michael@0 2962 }
michael@0 2963 }
michael@0 2964
michael@0 2965 } // namespace mozilla

mercurial