Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | // vim:cindent:ts=2:et:sw=2: |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* base class of all rendering objects */ |
michael@0 | 8 | |
michael@0 | 9 | #include "nsFrame.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/Attributes.h" |
michael@0 | 12 | #include "mozilla/DebugOnly.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "nsCOMPtr.h" |
michael@0 | 15 | #include "nsFrameList.h" |
michael@0 | 16 | #include "nsPlaceholderFrame.h" |
michael@0 | 17 | #include "nsIContent.h" |
michael@0 | 18 | #include "nsContentUtils.h" |
michael@0 | 19 | #include "nsIAtom.h" |
michael@0 | 20 | #include "nsString.h" |
michael@0 | 21 | #include "nsReadableUtils.h" |
michael@0 | 22 | #include "nsStyleContext.h" |
michael@0 | 23 | #include "nsTableOuterFrame.h" |
michael@0 | 24 | #include "nsView.h" |
michael@0 | 25 | #include "nsViewManager.h" |
michael@0 | 26 | #include "nsIScrollableFrame.h" |
michael@0 | 27 | #include "nsPresContext.h" |
michael@0 | 28 | #include "nsStyleConsts.h" |
michael@0 | 29 | #include "nsIPresShell.h" |
michael@0 | 30 | #include "prlog.h" |
michael@0 | 31 | #include "prprf.h" |
michael@0 | 32 | #include <stdarg.h> |
michael@0 | 33 | #include "nsFrameManager.h" |
michael@0 | 34 | #include "nsLayoutUtils.h" |
michael@0 | 35 | |
michael@0 | 36 | #include "nsIDOMNode.h" |
michael@0 | 37 | #include "nsISelection.h" |
michael@0 | 38 | #include "nsISelectionPrivate.h" |
michael@0 | 39 | #include "nsFrameSelection.h" |
michael@0 | 40 | #include "nsGkAtoms.h" |
michael@0 | 41 | #include "nsCSSAnonBoxes.h" |
michael@0 | 42 | |
michael@0 | 43 | #include "nsFrameTraversal.h" |
michael@0 | 44 | #include "nsRange.h" |
michael@0 | 45 | #include "nsITextControlFrame.h" |
michael@0 | 46 | #include "nsNameSpaceManager.h" |
michael@0 | 47 | #include "nsIPercentHeightObserver.h" |
michael@0 | 48 | #include "nsStyleStructInlines.h" |
michael@0 | 49 | #include <algorithm> |
michael@0 | 50 | |
michael@0 | 51 | #include "nsBidiPresUtils.h" |
michael@0 | 52 | |
michael@0 | 53 | // For triple-click pref |
michael@0 | 54 | #include "imgIContainer.h" |
michael@0 | 55 | #include "imgIRequest.h" |
michael@0 | 56 | #include "nsError.h" |
michael@0 | 57 | #include "nsContainerFrame.h" |
michael@0 | 58 | #include "nsBoxLayoutState.h" |
michael@0 | 59 | #include "nsBlockFrame.h" |
michael@0 | 60 | #include "nsDisplayList.h" |
michael@0 | 61 | #include "nsSVGIntegrationUtils.h" |
michael@0 | 62 | #include "nsSVGEffects.h" |
michael@0 | 63 | #include "nsChangeHint.h" |
michael@0 | 64 | #include "nsDeckFrame.h" |
michael@0 | 65 | #include "nsSubDocumentFrame.h" |
michael@0 | 66 | #include "SVGTextFrame.h" |
michael@0 | 67 | |
michael@0 | 68 | #include "gfxContext.h" |
michael@0 | 69 | #include "nsRenderingContext.h" |
michael@0 | 70 | #include "nsAbsoluteContainingBlock.h" |
michael@0 | 71 | #include "StickyScrollContainer.h" |
michael@0 | 72 | #include "nsFontInflationData.h" |
michael@0 | 73 | #include "gfxASurface.h" |
michael@0 | 74 | #include "nsRegion.h" |
michael@0 | 75 | #include "nsIFrameInlines.h" |
michael@0 | 76 | |
michael@0 | 77 | #include "mozilla/AsyncEventDispatcher.h" |
michael@0 | 78 | #include "mozilla/EventListenerManager.h" |
michael@0 | 79 | #include "mozilla/EventStateManager.h" |
michael@0 | 80 | #include "mozilla/EventStates.h" |
michael@0 | 81 | #include "mozilla/Preferences.h" |
michael@0 | 82 | #include "mozilla/LookAndFeel.h" |
michael@0 | 83 | #include "mozilla/MouseEvents.h" |
michael@0 | 84 | #include "mozilla/css/ImageLoader.h" |
michael@0 | 85 | #include "mozilla/gfx/Tools.h" |
michael@0 | 86 | #include "nsPrintfCString.h" |
michael@0 | 87 | |
michael@0 | 88 | using namespace mozilla; |
michael@0 | 89 | using namespace mozilla::css; |
michael@0 | 90 | using namespace mozilla::dom; |
michael@0 | 91 | using namespace mozilla::layers; |
michael@0 | 92 | using namespace mozilla::layout; |
michael@0 | 93 | |
michael@0 | 94 | // Struct containing cached metrics for box-wrapped frames. |
michael@0 | 95 | struct nsBoxLayoutMetrics |
michael@0 | 96 | { |
michael@0 | 97 | nsSize mPrefSize; |
michael@0 | 98 | nsSize mMinSize; |
michael@0 | 99 | nsSize mMaxSize; |
michael@0 | 100 | |
michael@0 | 101 | nsSize mBlockMinSize; |
michael@0 | 102 | nsSize mBlockPrefSize; |
michael@0 | 103 | nscoord mBlockAscent; |
michael@0 | 104 | |
michael@0 | 105 | nscoord mFlex; |
michael@0 | 106 | nscoord mAscent; |
michael@0 | 107 | |
michael@0 | 108 | nsSize mLastSize; |
michael@0 | 109 | }; |
michael@0 | 110 | |
michael@0 | 111 | struct nsContentAndOffset |
michael@0 | 112 | { |
michael@0 | 113 | nsIContent* mContent; |
michael@0 | 114 | int32_t mOffset; |
michael@0 | 115 | }; |
michael@0 | 116 | |
michael@0 | 117 | // Some Misc #defines |
michael@0 | 118 | #define SELECTION_DEBUG 0 |
michael@0 | 119 | #define FORCE_SELECTION_UPDATE 1 |
michael@0 | 120 | #define CALC_DEBUG 0 |
michael@0 | 121 | |
michael@0 | 122 | |
michael@0 | 123 | #include "nsILineIterator.h" |
michael@0 | 124 | |
michael@0 | 125 | //non Hack prototypes |
michael@0 | 126 | #if 0 |
michael@0 | 127 | static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent); |
michael@0 | 128 | #endif |
michael@0 | 129 | |
michael@0 | 130 | #include "prenv.h" |
michael@0 | 131 | |
michael@0 | 132 | // Formerly the nsIFrameDebug interface |
michael@0 | 133 | |
michael@0 | 134 | #ifdef DEBUG |
michael@0 | 135 | static bool gShowFrameBorders = false; |
michael@0 | 136 | |
michael@0 | 137 | void nsFrame::ShowFrameBorders(bool aEnable) |
michael@0 | 138 | { |
michael@0 | 139 | gShowFrameBorders = aEnable; |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | bool nsFrame::GetShowFrameBorders() |
michael@0 | 143 | { |
michael@0 | 144 | return gShowFrameBorders; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | static bool gShowEventTargetFrameBorder = false; |
michael@0 | 148 | |
michael@0 | 149 | void nsFrame::ShowEventTargetFrameBorder(bool aEnable) |
michael@0 | 150 | { |
michael@0 | 151 | gShowEventTargetFrameBorder = aEnable; |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | bool nsFrame::GetShowEventTargetFrameBorder() |
michael@0 | 155 | { |
michael@0 | 156 | return gShowEventTargetFrameBorder; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | /** |
michael@0 | 160 | * Note: the log module is created during library initialization which |
michael@0 | 161 | * means that you cannot perform logging before then. |
michael@0 | 162 | */ |
michael@0 | 163 | static PRLogModuleInfo* gLogModule; |
michael@0 | 164 | |
michael@0 | 165 | static PRLogModuleInfo* gStyleVerifyTreeLogModuleInfo; |
michael@0 | 166 | |
michael@0 | 167 | static uint32_t gStyleVerifyTreeEnable = 0x55; |
michael@0 | 168 | |
michael@0 | 169 | bool |
michael@0 | 170 | nsFrame::GetVerifyStyleTreeEnable() |
michael@0 | 171 | { |
michael@0 | 172 | if (gStyleVerifyTreeEnable == 0x55) { |
michael@0 | 173 | if (nullptr == gStyleVerifyTreeLogModuleInfo) { |
michael@0 | 174 | gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree"); |
michael@0 | 175 | gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level; |
michael@0 | 176 | } |
michael@0 | 177 | } |
michael@0 | 178 | return gStyleVerifyTreeEnable; |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | void |
michael@0 | 182 | nsFrame::SetVerifyStyleTreeEnable(bool aEnabled) |
michael@0 | 183 | { |
michael@0 | 184 | gStyleVerifyTreeEnable = aEnabled; |
michael@0 | 185 | } |
michael@0 | 186 | |
michael@0 | 187 | PRLogModuleInfo* |
michael@0 | 188 | nsFrame::GetLogModuleInfo() |
michael@0 | 189 | { |
michael@0 | 190 | if (nullptr == gLogModule) { |
michael@0 | 191 | gLogModule = PR_NewLogModule("frame"); |
michael@0 | 192 | } |
michael@0 | 193 | return gLogModule; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | #endif |
michael@0 | 197 | |
michael@0 | 198 | static void |
michael@0 | 199 | DestroyAbsoluteContainingBlock(void* aPropertyValue) |
michael@0 | 200 | { |
michael@0 | 201 | delete static_cast<nsAbsoluteContainingBlock*>(aPropertyValue); |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty, DestroyAbsoluteContainingBlock) |
michael@0 | 205 | |
michael@0 | 206 | bool |
michael@0 | 207 | nsIFrame::HasAbsolutelyPositionedChildren() const { |
michael@0 | 208 | return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames(); |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | nsAbsoluteContainingBlock* |
michael@0 | 212 | nsIFrame::GetAbsoluteContainingBlock() const { |
michael@0 | 213 | NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly"); |
michael@0 | 214 | nsAbsoluteContainingBlock* absCB = static_cast<nsAbsoluteContainingBlock*> |
michael@0 | 215 | (Properties().Get(AbsoluteContainingBlockProperty())); |
michael@0 | 216 | NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property"); |
michael@0 | 217 | return absCB; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | void |
michael@0 | 221 | nsIFrame::MarkAsAbsoluteContainingBlock() |
michael@0 | 222 | { |
michael@0 | 223 | MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); |
michael@0 | 224 | NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()), |
michael@0 | 225 | "Already has an abs-pos containing block property?"); |
michael@0 | 226 | NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), |
michael@0 | 227 | "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?"); |
michael@0 | 228 | AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); |
michael@0 | 229 | Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID())); |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | void |
michael@0 | 233 | nsIFrame::MarkAsNotAbsoluteContainingBlock() |
michael@0 | 234 | { |
michael@0 | 235 | NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!"); |
michael@0 | 236 | NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()), |
michael@0 | 237 | "Should have an abs-pos containing block property"); |
michael@0 | 238 | NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), |
michael@0 | 239 | "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit"); |
michael@0 | 240 | MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)); |
michael@0 | 241 | RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); |
michael@0 | 242 | Properties().Delete(AbsoluteContainingBlockProperty()); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | bool |
michael@0 | 246 | nsIFrame::CheckAndClearPaintedState() |
michael@0 | 247 | { |
michael@0 | 248 | bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES); |
michael@0 | 249 | RemoveStateBits(NS_FRAME_PAINTED_THEBES); |
michael@0 | 250 | |
michael@0 | 251 | nsIFrame::ChildListIterator lists(this); |
michael@0 | 252 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 253 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 254 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 255 | nsIFrame* child = childFrames.get(); |
michael@0 | 256 | if (child->CheckAndClearPaintedState()) { |
michael@0 | 257 | result = true; |
michael@0 | 258 | } |
michael@0 | 259 | } |
michael@0 | 260 | } |
michael@0 | 261 | return result; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | bool |
michael@0 | 265 | nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const |
michael@0 | 266 | { |
michael@0 | 267 | if (!StyleVisibility()->IsVisible()) { |
michael@0 | 268 | return false; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | const nsIFrame* frame = this; |
michael@0 | 272 | while (frame) { |
michael@0 | 273 | nsView* view = frame->GetView(); |
michael@0 | 274 | if (view && view->GetVisibility() == nsViewVisibility_kHide) |
michael@0 | 275 | return false; |
michael@0 | 276 | |
michael@0 | 277 | nsIFrame* parent = frame->GetParent(); |
michael@0 | 278 | nsDeckFrame* deck = do_QueryFrame(parent); |
michael@0 | 279 | if (deck) { |
michael@0 | 280 | if (deck->GetSelectedBox() != frame) |
michael@0 | 281 | return false; |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | if (parent) { |
michael@0 | 285 | frame = parent; |
michael@0 | 286 | } else { |
michael@0 | 287 | parent = nsLayoutUtils::GetCrossDocParentFrame(frame); |
michael@0 | 288 | if (!parent) |
michael@0 | 289 | break; |
michael@0 | 290 | |
michael@0 | 291 | if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 && |
michael@0 | 292 | parent->PresContext()->IsChrome() && !frame->PresContext()->IsChrome()) { |
michael@0 | 293 | break; |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | if (!parent->StyleVisibility()->IsVisible()) |
michael@0 | 297 | return false; |
michael@0 | 298 | |
michael@0 | 299 | frame = parent; |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | return true; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | void |
michael@0 | 307 | nsIFrame::FindCloserFrameForSelection( |
michael@0 | 308 | nsPoint aPoint, |
michael@0 | 309 | nsIFrame::FrameWithDistance* aCurrentBestFrame) |
michael@0 | 310 | { |
michael@0 | 311 | if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect, |
michael@0 | 312 | aCurrentBestFrame->mXDistance, |
michael@0 | 313 | aCurrentBestFrame->mYDistance)) { |
michael@0 | 314 | aCurrentBestFrame->mFrame = this; |
michael@0 | 315 | } |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | void |
michael@0 | 319 | nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) |
michael@0 | 320 | { |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | void |
michael@0 | 324 | NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, nsReflowStatus aSecondary) |
michael@0 | 325 | { |
michael@0 | 326 | *aPrimary |= aSecondary & |
michael@0 | 327 | (NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE | |
michael@0 | 328 | NS_FRAME_TRUNCATED | NS_FRAME_REFLOW_NEXTINFLOW); |
michael@0 | 329 | if (*aPrimary & NS_FRAME_NOT_COMPLETE) { |
michael@0 | 330 | *aPrimary &= ~NS_FRAME_OVERFLOW_INCOMPLETE; |
michael@0 | 331 | } |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | void |
michael@0 | 335 | nsWeakFrame::Init(nsIFrame* aFrame) |
michael@0 | 336 | { |
michael@0 | 337 | Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr); |
michael@0 | 338 | mFrame = aFrame; |
michael@0 | 339 | if (mFrame) { |
michael@0 | 340 | nsIPresShell* shell = mFrame->PresContext()->GetPresShell(); |
michael@0 | 341 | NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!"); |
michael@0 | 342 | if (shell) { |
michael@0 | 343 | shell->AddWeakFrame(this); |
michael@0 | 344 | } else { |
michael@0 | 345 | mFrame = nullptr; |
michael@0 | 346 | } |
michael@0 | 347 | } |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | nsIFrame* |
michael@0 | 351 | NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 352 | { |
michael@0 | 353 | return new (aPresShell) nsFrame(aContext); |
michael@0 | 354 | } |
michael@0 | 355 | |
michael@0 | 356 | nsFrame::nsFrame(nsStyleContext* aContext) |
michael@0 | 357 | { |
michael@0 | 358 | MOZ_COUNT_CTOR(nsFrame); |
michael@0 | 359 | |
michael@0 | 360 | mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY; |
michael@0 | 361 | mStyleContext = aContext; |
michael@0 | 362 | mStyleContext->AddRef(); |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | nsFrame::~nsFrame() |
michael@0 | 366 | { |
michael@0 | 367 | MOZ_COUNT_DTOR(nsFrame); |
michael@0 | 368 | |
michael@0 | 369 | NS_IF_RELEASE(mContent); |
michael@0 | 370 | mStyleContext->Release(); |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | NS_IMPL_FRAMEARENA_HELPERS(nsFrame) |
michael@0 | 374 | |
michael@0 | 375 | // Dummy operator delete. Will never be called, but must be defined |
michael@0 | 376 | // to satisfy some C++ ABIs. |
michael@0 | 377 | void |
michael@0 | 378 | nsFrame::operator delete(void *, size_t) |
michael@0 | 379 | { |
michael@0 | 380 | NS_RUNTIMEABORT("nsFrame::operator delete should never be called"); |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | NS_QUERYFRAME_HEAD(nsFrame) |
michael@0 | 384 | NS_QUERYFRAME_ENTRY(nsIFrame) |
michael@0 | 385 | NS_QUERYFRAME_TAIL_INHERITANCE_ROOT |
michael@0 | 386 | |
michael@0 | 387 | ///////////////////////////////////////////////////////////////////////////// |
michael@0 | 388 | // nsIFrame |
michael@0 | 389 | |
michael@0 | 390 | static bool |
michael@0 | 391 | IsFontSizeInflationContainer(nsIFrame* aFrame, |
michael@0 | 392 | const nsStyleDisplay* aStyleDisplay) |
michael@0 | 393 | { |
michael@0 | 394 | /* |
michael@0 | 395 | * Font size inflation is built around the idea that we're inflating |
michael@0 | 396 | * the fonts for a pan-and-zoom UI so that when the user scales up a |
michael@0 | 397 | * block or other container to fill the width of the device, the fonts |
michael@0 | 398 | * will be readable. To do this, we need to pick what counts as a |
michael@0 | 399 | * container. |
michael@0 | 400 | * |
michael@0 | 401 | * From a code perspective, the only hard requirement is that frames |
michael@0 | 402 | * that are line participants |
michael@0 | 403 | * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never |
michael@0 | 404 | * containers, since line layout assumes that the inflation is |
michael@0 | 405 | * consistent within a line. |
michael@0 | 406 | * |
michael@0 | 407 | * This is not an imposition, since we obviously want a bunch of text |
michael@0 | 408 | * (possibly with inline elements) flowing within a block to count the |
michael@0 | 409 | * block (or higher) as its container. |
michael@0 | 410 | * |
michael@0 | 411 | * We also want form controls, including the text in the anonymous |
michael@0 | 412 | * content inside of them, to match each other and the text next to |
michael@0 | 413 | * them, so they and their anonymous content should also not be a |
michael@0 | 414 | * container. |
michael@0 | 415 | * |
michael@0 | 416 | * However, because we can't reliably compute sizes across XUL during |
michael@0 | 417 | * reflow, any XUL frame with a XUL parent is always a container. |
michael@0 | 418 | * |
michael@0 | 419 | * There are contexts where it would be nice if some blocks didn't |
michael@0 | 420 | * count as a container, so that, for example, an indented quotation |
michael@0 | 421 | * didn't end up with a smaller font size. However, it's hard to |
michael@0 | 422 | * distinguish these situations where we really do want the indented |
michael@0 | 423 | * thing to count as a container, so we don't try, and blocks are |
michael@0 | 424 | * always containers. |
michael@0 | 425 | */ |
michael@0 | 426 | |
michael@0 | 427 | // The root frame should always be an inflation container. |
michael@0 | 428 | if (!aFrame->GetParent()) { |
michael@0 | 429 | return true; |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | nsIContent *content = aFrame->GetContent(); |
michael@0 | 433 | bool isInline = (aFrame->GetDisplay() == NS_STYLE_DISPLAY_INLINE || |
michael@0 | 434 | (aFrame->IsFloating() && |
michael@0 | 435 | aFrame->GetType() == nsGkAtoms::letterFrame) || |
michael@0 | 436 | // Given multiple frames for the same node, only the |
michael@0 | 437 | // outer one should be considered a container. |
michael@0 | 438 | // (Important, e.g., for nsSelectsAreaFrame.) |
michael@0 | 439 | (aFrame->GetParent()->GetContent() == content) || |
michael@0 | 440 | (content && (content->IsHTML(nsGkAtoms::option) || |
michael@0 | 441 | content->IsHTML(nsGkAtoms::optgroup) || |
michael@0 | 442 | content->IsHTML(nsGkAtoms::select) || |
michael@0 | 443 | content->IsInNativeAnonymousSubtree()))) && |
michael@0 | 444 | !(aFrame->IsBoxFrame() && aFrame->GetParent()->IsBoxFrame()); |
michael@0 | 445 | NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || |
michael@0 | 446 | isInline || |
michael@0 | 447 | // br frames and mathml frames report being line |
michael@0 | 448 | // participants even when their position or display is |
michael@0 | 449 | // set |
michael@0 | 450 | aFrame->GetType() == nsGkAtoms::brFrame || |
michael@0 | 451 | aFrame->IsFrameOfType(nsIFrame::eMathML), |
michael@0 | 452 | "line participants must not be containers"); |
michael@0 | 453 | NS_ASSERTION(aFrame->GetType() != nsGkAtoms::bulletFrame || isInline, |
michael@0 | 454 | "bullets should not be containers"); |
michael@0 | 455 | return !isInline; |
michael@0 | 456 | } |
michael@0 | 457 | |
michael@0 | 458 | void |
michael@0 | 459 | nsFrame::Init(nsIContent* aContent, |
michael@0 | 460 | nsIFrame* aParent, |
michael@0 | 461 | nsIFrame* aPrevInFlow) |
michael@0 | 462 | { |
michael@0 | 463 | NS_PRECONDITION(!mContent, "Double-initing a frame?"); |
michael@0 | 464 | NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && |
michael@0 | 465 | !IsFrameOfType(eDEBUGNoFrames), |
michael@0 | 466 | "IsFrameOfType implementation that doesn't call base class"); |
michael@0 | 467 | |
michael@0 | 468 | mContent = aContent; |
michael@0 | 469 | mParent = aParent; |
michael@0 | 470 | |
michael@0 | 471 | if (aContent) { |
michael@0 | 472 | NS_ADDREF(aContent); |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | if (aPrevInFlow) { |
michael@0 | 476 | // Make sure the general flags bits are the same |
michael@0 | 477 | nsFrameState state = aPrevInFlow->GetStateBits(); |
michael@0 | 478 | |
michael@0 | 479 | // Make bits that are currently off (see constructor) the same: |
michael@0 | 480 | mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | |
michael@0 | 481 | NS_FRAME_PART_OF_IBSPLIT | |
michael@0 | 482 | NS_FRAME_MAY_BE_TRANSFORMED | |
michael@0 | 483 | NS_FRAME_MAY_HAVE_GENERATED_CONTENT | |
michael@0 | 484 | NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); |
michael@0 | 485 | } |
michael@0 | 486 | if (mParent) { |
michael@0 | 487 | nsFrameState state = mParent->GetStateBits(); |
michael@0 | 488 | |
michael@0 | 489 | // Make bits that are currently off (see constructor) the same: |
michael@0 | 490 | mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | |
michael@0 | 491 | NS_FRAME_GENERATED_CONTENT | |
michael@0 | 492 | NS_FRAME_IS_SVG_TEXT | |
michael@0 | 493 | NS_FRAME_IN_POPUP | |
michael@0 | 494 | NS_FRAME_IS_NONDISPLAY); |
michael@0 | 495 | } |
michael@0 | 496 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 497 | if (disp->HasTransform(this)) { |
michael@0 | 498 | // The frame gets reconstructed if we toggle the -moz-transform |
michael@0 | 499 | // property, so we can set this bit here and then ignore it. |
michael@0 | 500 | mState |= NS_FRAME_MAY_BE_TRANSFORMED; |
michael@0 | 501 | } |
michael@0 | 502 | if (disp->mPosition == NS_STYLE_POSITION_STICKY && |
michael@0 | 503 | !aPrevInFlow && |
michael@0 | 504 | !(mState & NS_FRAME_IS_NONDISPLAY) && |
michael@0 | 505 | !disp->IsInnerTableStyle()) { |
michael@0 | 506 | // Note that we only add first continuations, but we really only |
michael@0 | 507 | // want to add first continuation-or-ib-split-siblings. But since we |
michael@0 | 508 | // don't yet know if we're a later part of a block-in-inline split, |
michael@0 | 509 | // we'll just add later members of a block-in-inline split here, and |
michael@0 | 510 | // then StickyScrollContainer will remove them later. |
michael@0 | 511 | // We don't currently support relative positioning of inner table |
michael@0 | 512 | // elements (bug 35168), so exclude them from sticky positioning too. |
michael@0 | 513 | StickyScrollContainer* ssc = |
michael@0 | 514 | StickyScrollContainer::GetStickyScrollContainerForFrame(this); |
michael@0 | 515 | if (ssc) { |
michael@0 | 516 | ssc->AddFrame(this); |
michael@0 | 517 | } |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent() |
michael@0 | 521 | #ifdef DEBUG |
michael@0 | 522 | // We have assertions that check inflation invariants even when |
michael@0 | 523 | // font size inflation is not enabled. |
michael@0 | 524 | || true |
michael@0 | 525 | #endif |
michael@0 | 526 | ) { |
michael@0 | 527 | if (IsFontSizeInflationContainer(this, disp)) { |
michael@0 | 528 | AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER); |
michael@0 | 529 | if (!GetParent() || |
michael@0 | 530 | // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet. |
michael@0 | 531 | disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this)) { |
michael@0 | 532 | AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
michael@0 | 533 | } |
michael@0 | 534 | } |
michael@0 | 535 | NS_ASSERTION(GetParent() || |
michael@0 | 536 | (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER), |
michael@0 | 537 | "root frame should always be a container"); |
michael@0 | 538 | } |
michael@0 | 539 | |
michael@0 | 540 | DidSetStyleContext(nullptr); |
michael@0 | 541 | |
michael@0 | 542 | if (IsBoxWrapped()) |
michael@0 | 543 | InitBoxMetrics(false); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | nsresult nsFrame::SetInitialChildList(ChildListID aListID, |
michael@0 | 547 | nsFrameList& aChildList) |
michael@0 | 548 | { |
michael@0 | 549 | // XXX This shouldn't be getting called at all, but currently is for backwards |
michael@0 | 550 | // compatility reasons... |
michael@0 | 551 | #if 0 |
michael@0 | 552 | NS_ERROR("not a container"); |
michael@0 | 553 | return NS_ERROR_UNEXPECTED; |
michael@0 | 554 | #else |
michael@0 | 555 | NS_ASSERTION(aChildList.IsEmpty(), "not a container"); |
michael@0 | 556 | return NS_OK; |
michael@0 | 557 | #endif |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | nsresult |
michael@0 | 561 | nsFrame::AppendFrames(ChildListID aListID, |
michael@0 | 562 | nsFrameList& aFrameList) |
michael@0 | 563 | { |
michael@0 | 564 | NS_PRECONDITION(false, "not a container"); |
michael@0 | 565 | return NS_ERROR_UNEXPECTED; |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | nsresult |
michael@0 | 569 | nsFrame::InsertFrames(ChildListID aListID, |
michael@0 | 570 | nsIFrame* aPrevFrame, |
michael@0 | 571 | nsFrameList& aFrameList) |
michael@0 | 572 | { |
michael@0 | 573 | NS_PRECONDITION(false, "not a container"); |
michael@0 | 574 | return NS_ERROR_UNEXPECTED; |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | nsresult |
michael@0 | 578 | nsFrame::RemoveFrame(ChildListID aListID, |
michael@0 | 579 | nsIFrame* aOldFrame) |
michael@0 | 580 | { |
michael@0 | 581 | NS_PRECONDITION(false, "not a container"); |
michael@0 | 582 | return NS_ERROR_UNEXPECTED; |
michael@0 | 583 | } |
michael@0 | 584 | |
michael@0 | 585 | void |
michael@0 | 586 | nsFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 587 | { |
michael@0 | 588 | NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), |
michael@0 | 589 | "destroy called on frame while scripts not blocked"); |
michael@0 | 590 | NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(), |
michael@0 | 591 | "Frames should be removed before destruction."); |
michael@0 | 592 | NS_ASSERTION(aDestructRoot, "Must specify destruct root"); |
michael@0 | 593 | MOZ_ASSERT(!HasAbsolutelyPositionedChildren()); |
michael@0 | 594 | |
michael@0 | 595 | nsSVGEffects::InvalidateDirectRenderingObservers(this); |
michael@0 | 596 | |
michael@0 | 597 | if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) { |
michael@0 | 598 | StickyScrollContainer* ssc = |
michael@0 | 599 | StickyScrollContainer::GetStickyScrollContainerForFrame(this); |
michael@0 | 600 | if (ssc) { |
michael@0 | 601 | ssc->RemoveFrame(this); |
michael@0 | 602 | } |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | // Get the view pointer now before the frame properties disappear |
michael@0 | 606 | // when we call NotifyDestroyingFrame() |
michael@0 | 607 | nsView* view = GetView(); |
michael@0 | 608 | nsPresContext* presContext = PresContext(); |
michael@0 | 609 | |
michael@0 | 610 | nsIPresShell *shell = presContext->GetPresShell(); |
michael@0 | 611 | if (mState & NS_FRAME_OUT_OF_FLOW) { |
michael@0 | 612 | nsPlaceholderFrame* placeholder = |
michael@0 | 613 | shell->FrameManager()->GetPlaceholderFrameFor(this); |
michael@0 | 614 | NS_ASSERTION(!placeholder || (aDestructRoot != this), |
michael@0 | 615 | "Don't call Destroy() on OOFs, call Destroy() on the placeholder."); |
michael@0 | 616 | NS_ASSERTION(!placeholder || |
michael@0 | 617 | nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder), |
michael@0 | 618 | "Placeholder relationship should have been torn down already; " |
michael@0 | 619 | "this might mean we have a stray placeholder in the tree."); |
michael@0 | 620 | if (placeholder) { |
michael@0 | 621 | shell->FrameManager()->UnregisterPlaceholderFrame(placeholder); |
michael@0 | 622 | placeholder->SetOutOfFlowFrame(nullptr); |
michael@0 | 623 | } |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | // If we have any IB split siblings, clear their references to us. |
michael@0 | 627 | // (Note: This has to happen before we call shell->NotifyDestroyingFrame, |
michael@0 | 628 | // because that clears our Properties() table.) |
michael@0 | 629 | if (mState & NS_FRAME_PART_OF_IBSPLIT) { |
michael@0 | 630 | // Delete previous sibling's reference to me. |
michael@0 | 631 | nsIFrame* prevSib = static_cast<nsIFrame*> |
michael@0 | 632 | (Properties().Get(nsIFrame::IBSplitPrevSibling())); |
michael@0 | 633 | if (prevSib) { |
michael@0 | 634 | NS_WARN_IF_FALSE(this == |
michael@0 | 635 | prevSib->Properties().Get(nsIFrame::IBSplitSibling()), |
michael@0 | 636 | "IB sibling chain is inconsistent"); |
michael@0 | 637 | prevSib->Properties().Delete(nsIFrame::IBSplitSibling()); |
michael@0 | 638 | } |
michael@0 | 639 | |
michael@0 | 640 | // Delete next sibling's reference to me. |
michael@0 | 641 | nsIFrame* nextSib = static_cast<nsIFrame*> |
michael@0 | 642 | (Properties().Get(nsIFrame::IBSplitSibling())); |
michael@0 | 643 | if (nextSib) { |
michael@0 | 644 | NS_WARN_IF_FALSE(this == |
michael@0 | 645 | nextSib->Properties().Get(nsIFrame::IBSplitPrevSibling()), |
michael@0 | 646 | "IB sibling chain is inconsistent"); |
michael@0 | 647 | nextSib->Properties().Delete(nsIFrame::IBSplitPrevSibling()); |
michael@0 | 648 | } |
michael@0 | 649 | } |
michael@0 | 650 | |
michael@0 | 651 | shell->NotifyDestroyingFrame(this); |
michael@0 | 652 | |
michael@0 | 653 | if (mState & NS_FRAME_EXTERNAL_REFERENCE) { |
michael@0 | 654 | shell->ClearFrameRefs(this); |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | if (view) { |
michael@0 | 658 | // Break association between view and frame |
michael@0 | 659 | view->SetFrame(nullptr); |
michael@0 | 660 | |
michael@0 | 661 | // Destroy the view |
michael@0 | 662 | view->Destroy(); |
michael@0 | 663 | } |
michael@0 | 664 | |
michael@0 | 665 | // Make sure that our deleted frame can't be returned from GetPrimaryFrame() |
michael@0 | 666 | if (mContent && mContent->GetPrimaryFrame() == this) { |
michael@0 | 667 | mContent->SetPrimaryFrame(nullptr); |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | // Must retrieve the object ID before calling destructors, so the |
michael@0 | 671 | // vtable is still valid. |
michael@0 | 672 | // |
michael@0 | 673 | // Note to future tweakers: having the method that returns the |
michael@0 | 674 | // object size call the destructor will not avoid an indirect call; |
michael@0 | 675 | // the compiler cannot devirtualize the call to the destructor even |
michael@0 | 676 | // if it's from a method defined in the same class. |
michael@0 | 677 | |
michael@0 | 678 | nsQueryFrame::FrameIID id = GetFrameId(); |
michael@0 | 679 | this->~nsFrame(); |
michael@0 | 680 | |
michael@0 | 681 | // Now that we're totally cleaned out, we need to add ourselves to |
michael@0 | 682 | // the presshell's recycler. |
michael@0 | 683 | shell->FreeFrame(id, this); |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | nsresult |
michael@0 | 687 | nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const |
michael@0 | 688 | { |
michael@0 | 689 | aStart = 0; |
michael@0 | 690 | aEnd = 0; |
michael@0 | 691 | return NS_OK; |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | // Subclass hook for style post processing |
michael@0 | 695 | /* virtual */ void |
michael@0 | 696 | nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
michael@0 | 697 | { |
michael@0 | 698 | if (IsSVGText()) { |
michael@0 | 699 | SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>( |
michael@0 | 700 | nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame)); |
michael@0 | 701 | nsIFrame* anonBlock = svgTextFrame->GetFirstPrincipalChild(); |
michael@0 | 702 | // Just as in SVGTextFrame::DidSetStyleContext, we need to ensure that |
michael@0 | 703 | // any non-display SVGTextFrames get reflowed when a child text frame |
michael@0 | 704 | // gets new style. |
michael@0 | 705 | // |
michael@0 | 706 | // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's |
michael@0 | 707 | // anonymous block frame rather than our self, since NS_FRAME_FIRST_REFLOW |
michael@0 | 708 | // may be set on us if we're a new frame that has been inserted after the |
michael@0 | 709 | // document's first reflow. (In which case this DidSetStyleContext call may |
michael@0 | 710 | // be happening under frame construction under a Reflow() call.) |
michael@0 | 711 | if (anonBlock && !(anonBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 712 | (svgTextFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) && |
michael@0 | 713 | !(svgTextFrame->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW)) { |
michael@0 | 714 | svgTextFrame->ScheduleReflowSVGNonDisplayText(); |
michael@0 | 715 | } |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader(); |
michael@0 | 719 | |
michael@0 | 720 | // If the old context had a background image image and new context |
michael@0 | 721 | // does not have the same image, clear the image load notifier |
michael@0 | 722 | // (which keeps the image loading, if it still is) for the frame. |
michael@0 | 723 | // We want to do this conservatively because some frames paint their |
michael@0 | 724 | // backgrounds from some other frame's style data, and we don't want |
michael@0 | 725 | // to clear those notifiers unless we have to. (They'll be reset |
michael@0 | 726 | // when we paint, although we could miss a notification in that |
michael@0 | 727 | // interval.) |
michael@0 | 728 | const nsStyleBackground *oldBG = aOldStyleContext ? |
michael@0 | 729 | aOldStyleContext->StyleBackground() : |
michael@0 | 730 | nullptr; |
michael@0 | 731 | const nsStyleBackground *newBG = StyleBackground(); |
michael@0 | 732 | if (oldBG) { |
michael@0 | 733 | NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) { |
michael@0 | 734 | // If there is an image in oldBG that's not in newBG, drop it. |
michael@0 | 735 | if (i >= newBG->mImageCount || |
michael@0 | 736 | !oldBG->mLayers[i].mImage.ImageDataEquals(newBG->mLayers[i].mImage)) { |
michael@0 | 737 | const nsStyleImage& oldImage = oldBG->mLayers[i].mImage; |
michael@0 | 738 | if (oldImage.GetType() != eStyleImageType_Image) { |
michael@0 | 739 | continue; |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(), |
michael@0 | 743 | this); |
michael@0 | 744 | } |
michael@0 | 745 | } |
michael@0 | 746 | } |
michael@0 | 747 | |
michael@0 | 748 | NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, newBG) { |
michael@0 | 749 | // If there is an image in newBG that's not in oldBG, add it. |
michael@0 | 750 | if (!oldBG || i >= oldBG->mImageCount || |
michael@0 | 751 | !newBG->mLayers[i].mImage.ImageDataEquals(oldBG->mLayers[i].mImage)) { |
michael@0 | 752 | const nsStyleImage& newImage = newBG->mLayers[i].mImage; |
michael@0 | 753 | if (newImage.GetType() != eStyleImageType_Image) { |
michael@0 | 754 | continue; |
michael@0 | 755 | } |
michael@0 | 756 | |
michael@0 | 757 | imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this); |
michael@0 | 758 | } |
michael@0 | 759 | } |
michael@0 | 760 | |
michael@0 | 761 | if (aOldStyleContext) { |
michael@0 | 762 | // If we detect a change on margin, padding or border, we store the old |
michael@0 | 763 | // values on the frame itself between now and reflow, so if someone |
michael@0 | 764 | // calls GetUsed(Margin|Border|Padding)() before the next reflow, we |
michael@0 | 765 | // can give an accurate answer. |
michael@0 | 766 | // We don't want to set the property if one already exists. |
michael@0 | 767 | FrameProperties props = Properties(); |
michael@0 | 768 | nsMargin oldValue(0, 0, 0, 0); |
michael@0 | 769 | nsMargin newValue(0, 0, 0, 0); |
michael@0 | 770 | const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin(); |
michael@0 | 771 | if (oldMargin && oldMargin->GetMargin(oldValue)) { |
michael@0 | 772 | if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) && |
michael@0 | 773 | !props.Get(UsedMarginProperty())) { |
michael@0 | 774 | props.Set(UsedMarginProperty(), new nsMargin(oldValue)); |
michael@0 | 775 | } |
michael@0 | 776 | } |
michael@0 | 777 | |
michael@0 | 778 | const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding(); |
michael@0 | 779 | if (oldPadding && oldPadding->GetPadding(oldValue)) { |
michael@0 | 780 | if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) && |
michael@0 | 781 | !props.Get(UsedPaddingProperty())) { |
michael@0 | 782 | props.Set(UsedPaddingProperty(), new nsMargin(oldValue)); |
michael@0 | 783 | } |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder(); |
michael@0 | 787 | if (oldBorder) { |
michael@0 | 788 | oldValue = oldBorder->GetComputedBorder(); |
michael@0 | 789 | newValue = StyleBorder()->GetComputedBorder(); |
michael@0 | 790 | if (oldValue != newValue && |
michael@0 | 791 | !props.Get(UsedBorderProperty())) { |
michael@0 | 792 | props.Set(UsedBorderProperty(), new nsMargin(oldValue)); |
michael@0 | 793 | } |
michael@0 | 794 | } |
michael@0 | 795 | } |
michael@0 | 796 | |
michael@0 | 797 | imgIRequest *oldBorderImage = aOldStyleContext |
michael@0 | 798 | ? aOldStyleContext->StyleBorder()->GetBorderImageRequest() |
michael@0 | 799 | : nullptr; |
michael@0 | 800 | imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest(); |
michael@0 | 801 | // FIXME (Bug 759996): The following is no longer true. |
michael@0 | 802 | // For border-images, we can't be as conservative (we need to set the |
michael@0 | 803 | // new loaders if there has been any change) since the CalcDifference |
michael@0 | 804 | // call depended on the result of GetComputedBorder() and that result |
michael@0 | 805 | // depends on whether the image has loaded, start the image load now |
michael@0 | 806 | // so that we'll get notified when it completes loading and can do a |
michael@0 | 807 | // restyle. Otherwise, the image might finish loading from the |
michael@0 | 808 | // network before we start listening to its notifications, and then |
michael@0 | 809 | // we'll never know that it's finished loading. Likewise, we want to |
michael@0 | 810 | // do this for freshly-created frames to prevent a similar race if the |
michael@0 | 811 | // image loads between reflow (which can depend on whether the image |
michael@0 | 812 | // is loaded) and paint. We also don't really care about any callers |
michael@0 | 813 | // who try to paint borders with a different style context, because |
michael@0 | 814 | // they won't have the correct size for the border either. |
michael@0 | 815 | if (oldBorderImage != newBorderImage) { |
michael@0 | 816 | // stop and restart the image loading/notification |
michael@0 | 817 | if (oldBorderImage) { |
michael@0 | 818 | imageLoader->DisassociateRequestFromFrame(oldBorderImage, this); |
michael@0 | 819 | } |
michael@0 | 820 | if (newBorderImage) { |
michael@0 | 821 | imageLoader->AssociateRequestToFrame(newBorderImage, this); |
michael@0 | 822 | } |
michael@0 | 823 | } |
michael@0 | 824 | |
michael@0 | 825 | // If the page contains markup that overrides text direction, and |
michael@0 | 826 | // does not contain any characters that would activate the Unicode |
michael@0 | 827 | // bidi algorithm, we need to call |SetBidiEnabled| on the pres |
michael@0 | 828 | // context before reflow starts. See bug 115921. |
michael@0 | 829 | if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
michael@0 | 830 | PresContext()->SetBidiEnabled(); |
michael@0 | 831 | } |
michael@0 | 832 | } |
michael@0 | 833 | |
michael@0 | 834 | // MSVC fails with link error "one or more multiply defined symbols found", |
michael@0 | 835 | // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined" |
michael@0 | 836 | // etc if they are not defined. |
michael@0 | 837 | #ifndef _MSC_VER |
michael@0 | 838 | // static nsIFrame constants; initialized in the header file. |
michael@0 | 839 | const nsIFrame::ChildListID nsIFrame::kPrincipalList; |
michael@0 | 840 | const nsIFrame::ChildListID nsIFrame::kAbsoluteList; |
michael@0 | 841 | const nsIFrame::ChildListID nsIFrame::kBulletList; |
michael@0 | 842 | const nsIFrame::ChildListID nsIFrame::kCaptionList; |
michael@0 | 843 | const nsIFrame::ChildListID nsIFrame::kColGroupList; |
michael@0 | 844 | const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList; |
michael@0 | 845 | const nsIFrame::ChildListID nsIFrame::kFixedList; |
michael@0 | 846 | const nsIFrame::ChildListID nsIFrame::kFloatList; |
michael@0 | 847 | const nsIFrame::ChildListID nsIFrame::kOverflowContainersList; |
michael@0 | 848 | const nsIFrame::ChildListID nsIFrame::kOverflowList; |
michael@0 | 849 | const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList; |
michael@0 | 850 | const nsIFrame::ChildListID nsIFrame::kPopupList; |
michael@0 | 851 | const nsIFrame::ChildListID nsIFrame::kPushedFloatsList; |
michael@0 | 852 | const nsIFrame::ChildListID nsIFrame::kSelectPopupList; |
michael@0 | 853 | const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList; |
michael@0 | 854 | #endif |
michael@0 | 855 | |
michael@0 | 856 | /* virtual */ nsMargin |
michael@0 | 857 | nsIFrame::GetUsedMargin() const |
michael@0 | 858 | { |
michael@0 | 859 | nsMargin margin(0, 0, 0, 0); |
michael@0 | 860 | if (((mState & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 861 | !(mState & NS_FRAME_IN_REFLOW)) || |
michael@0 | 862 | IsSVGText()) |
michael@0 | 863 | return margin; |
michael@0 | 864 | |
michael@0 | 865 | nsMargin *m = static_cast<nsMargin*> |
michael@0 | 866 | (Properties().Get(UsedMarginProperty())); |
michael@0 | 867 | if (m) { |
michael@0 | 868 | margin = *m; |
michael@0 | 869 | } else { |
michael@0 | 870 | DebugOnly<bool> hasMargin = StyleMargin()->GetMargin(margin); |
michael@0 | 871 | NS_ASSERTION(hasMargin, "We should have a margin here! (out of memory?)"); |
michael@0 | 872 | } |
michael@0 | 873 | return margin; |
michael@0 | 874 | } |
michael@0 | 875 | |
michael@0 | 876 | /* virtual */ nsMargin |
michael@0 | 877 | nsIFrame::GetUsedBorder() const |
michael@0 | 878 | { |
michael@0 | 879 | nsMargin border(0, 0, 0, 0); |
michael@0 | 880 | if (((mState & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 881 | !(mState & NS_FRAME_IN_REFLOW)) || |
michael@0 | 882 | IsSVGText()) |
michael@0 | 883 | return border; |
michael@0 | 884 | |
michael@0 | 885 | // Theme methods don't use const-ness. |
michael@0 | 886 | nsIFrame *mutable_this = const_cast<nsIFrame*>(this); |
michael@0 | 887 | |
michael@0 | 888 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 889 | if (mutable_this->IsThemed(disp)) { |
michael@0 | 890 | nsIntMargin result; |
michael@0 | 891 | nsPresContext *presContext = PresContext(); |
michael@0 | 892 | presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), |
michael@0 | 893 | mutable_this, disp->mAppearance, |
michael@0 | 894 | &result); |
michael@0 | 895 | border.left = presContext->DevPixelsToAppUnits(result.left); |
michael@0 | 896 | border.top = presContext->DevPixelsToAppUnits(result.top); |
michael@0 | 897 | border.right = presContext->DevPixelsToAppUnits(result.right); |
michael@0 | 898 | border.bottom = presContext->DevPixelsToAppUnits(result.bottom); |
michael@0 | 899 | return border; |
michael@0 | 900 | } |
michael@0 | 901 | |
michael@0 | 902 | nsMargin *b = static_cast<nsMargin*> |
michael@0 | 903 | (Properties().Get(UsedBorderProperty())); |
michael@0 | 904 | if (b) { |
michael@0 | 905 | border = *b; |
michael@0 | 906 | } else { |
michael@0 | 907 | border = StyleBorder()->GetComputedBorder(); |
michael@0 | 908 | } |
michael@0 | 909 | return border; |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | /* virtual */ nsMargin |
michael@0 | 913 | nsIFrame::GetUsedPadding() const |
michael@0 | 914 | { |
michael@0 | 915 | nsMargin padding(0, 0, 0, 0); |
michael@0 | 916 | if (((mState & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 917 | !(mState & NS_FRAME_IN_REFLOW)) || |
michael@0 | 918 | IsSVGText()) |
michael@0 | 919 | return padding; |
michael@0 | 920 | |
michael@0 | 921 | // Theme methods don't use const-ness. |
michael@0 | 922 | nsIFrame *mutable_this = const_cast<nsIFrame*>(this); |
michael@0 | 923 | |
michael@0 | 924 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 925 | if (mutable_this->IsThemed(disp)) { |
michael@0 | 926 | nsPresContext *presContext = PresContext(); |
michael@0 | 927 | nsIntMargin widget; |
michael@0 | 928 | if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), |
michael@0 | 929 | mutable_this, |
michael@0 | 930 | disp->mAppearance, |
michael@0 | 931 | &widget)) { |
michael@0 | 932 | padding.top = presContext->DevPixelsToAppUnits(widget.top); |
michael@0 | 933 | padding.right = presContext->DevPixelsToAppUnits(widget.right); |
michael@0 | 934 | padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom); |
michael@0 | 935 | padding.left = presContext->DevPixelsToAppUnits(widget.left); |
michael@0 | 936 | return padding; |
michael@0 | 937 | } |
michael@0 | 938 | } |
michael@0 | 939 | |
michael@0 | 940 | nsMargin *p = static_cast<nsMargin*> |
michael@0 | 941 | (Properties().Get(UsedPaddingProperty())); |
michael@0 | 942 | if (p) { |
michael@0 | 943 | padding = *p; |
michael@0 | 944 | } else { |
michael@0 | 945 | DebugOnly<bool> hasPadding = StylePadding()->GetPadding(padding); |
michael@0 | 946 | NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)"); |
michael@0 | 947 | } |
michael@0 | 948 | return padding; |
michael@0 | 949 | } |
michael@0 | 950 | |
michael@0 | 951 | int |
michael@0 | 952 | nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const |
michael@0 | 953 | { |
michael@0 | 954 | // Convert the logical skip sides to physical sides using the frame's |
michael@0 | 955 | // writing mode |
michael@0 | 956 | WritingMode writingMode = GetWritingMode(); |
michael@0 | 957 | int logicalSkip = GetLogicalSkipSides(aReflowState); |
michael@0 | 958 | int skip = 0; |
michael@0 | 959 | |
michael@0 | 960 | if (logicalSkip & LOGICAL_SIDE_B_START) { |
michael@0 | 961 | if (writingMode.IsVertical()) { |
michael@0 | 962 | skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT); |
michael@0 | 963 | } else { |
michael@0 | 964 | skip |= 1 << NS_SIDE_TOP; |
michael@0 | 965 | } |
michael@0 | 966 | } |
michael@0 | 967 | |
michael@0 | 968 | if (logicalSkip & LOGICAL_SIDE_B_END) { |
michael@0 | 969 | if (writingMode.IsVertical()) { |
michael@0 | 970 | skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT); |
michael@0 | 971 | } else { |
michael@0 | 972 | skip |= 1 << NS_SIDE_BOTTOM; |
michael@0 | 973 | } |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | if (logicalSkip & LOGICAL_SIDE_I_START) { |
michael@0 | 977 | if (writingMode.IsVertical()) { |
michael@0 | 978 | skip |= 1 << NS_SIDE_TOP; |
michael@0 | 979 | } else { |
michael@0 | 980 | skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT); |
michael@0 | 981 | } |
michael@0 | 982 | } |
michael@0 | 983 | |
michael@0 | 984 | if (logicalSkip & LOGICAL_SIDE_I_END) { |
michael@0 | 985 | if (writingMode.IsVertical()) { |
michael@0 | 986 | skip |= 1 << NS_SIDE_BOTTOM; |
michael@0 | 987 | } else { |
michael@0 | 988 | skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT); |
michael@0 | 989 | } |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | return skip; |
michael@0 | 993 | } |
michael@0 | 994 | |
michael@0 | 995 | |
michael@0 | 996 | void |
michael@0 | 997 | nsIFrame::ApplySkipSides(nsMargin& aMargin, |
michael@0 | 998 | const nsHTMLReflowState* aReflowState) const |
michael@0 | 999 | { |
michael@0 | 1000 | int skipSides = GetSkipSides(aReflowState); |
michael@0 | 1001 | if (skipSides & (1 << NS_SIDE_TOP)) { |
michael@0 | 1002 | aMargin.top = 0; |
michael@0 | 1003 | } |
michael@0 | 1004 | if (skipSides & (1 << NS_SIDE_RIGHT)) { |
michael@0 | 1005 | aMargin.right = 0; |
michael@0 | 1006 | } |
michael@0 | 1007 | if (skipSides & (1 << NS_SIDE_BOTTOM)) { |
michael@0 | 1008 | aMargin.bottom = 0; |
michael@0 | 1009 | } |
michael@0 | 1010 | if (skipSides & (1 << NS_SIDE_LEFT)) { |
michael@0 | 1011 | aMargin.left = 0; |
michael@0 | 1012 | } |
michael@0 | 1013 | } |
michael@0 | 1014 | |
michael@0 | 1015 | void |
michael@0 | 1016 | nsIFrame::ApplyLogicalSkipSides(LogicalMargin& aMargin, |
michael@0 | 1017 | const nsHTMLReflowState* aReflowState) const |
michael@0 | 1018 | { |
michael@0 | 1019 | int skipSides = GetLogicalSkipSides(aReflowState); |
michael@0 | 1020 | if (skipSides & (LOGICAL_SIDE_B_START)) { |
michael@0 | 1021 | aMargin.BStart(GetWritingMode()) = 0; |
michael@0 | 1022 | } |
michael@0 | 1023 | if (skipSides & (LOGICAL_SIDE_I_END)) { |
michael@0 | 1024 | aMargin.IEnd(GetWritingMode()) = 0; |
michael@0 | 1025 | } |
michael@0 | 1026 | if (skipSides & (LOGICAL_SIDE_B_END)) { |
michael@0 | 1027 | aMargin.BEnd(GetWritingMode()) = 0; |
michael@0 | 1028 | } |
michael@0 | 1029 | if (skipSides & (LOGICAL_SIDE_I_START)) { |
michael@0 | 1030 | aMargin.IStart(GetWritingMode()) = 0; |
michael@0 | 1031 | } |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | nsRect |
michael@0 | 1035 | nsIFrame::GetPaddingRectRelativeToSelf() const |
michael@0 | 1036 | { |
michael@0 | 1037 | nsMargin border(GetUsedBorder()); |
michael@0 | 1038 | ApplySkipSides(border); |
michael@0 | 1039 | nsRect r(0, 0, mRect.width, mRect.height); |
michael@0 | 1040 | r.Deflate(border); |
michael@0 | 1041 | return r; |
michael@0 | 1042 | } |
michael@0 | 1043 | |
michael@0 | 1044 | nsRect |
michael@0 | 1045 | nsIFrame::GetPaddingRect() const |
michael@0 | 1046 | { |
michael@0 | 1047 | return GetPaddingRectRelativeToSelf() + GetPosition(); |
michael@0 | 1048 | } |
michael@0 | 1049 | |
michael@0 | 1050 | WritingMode |
michael@0 | 1051 | nsIFrame::GetWritingMode(nsIFrame* aSubFrame) const |
michael@0 | 1052 | { |
michael@0 | 1053 | WritingMode writingMode = GetWritingMode(); |
michael@0 | 1054 | |
michael@0 | 1055 | if (!writingMode.IsVertical() && |
michael@0 | 1056 | (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT)) { |
michael@0 | 1057 | nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame); |
michael@0 | 1058 | writingMode.SetDirectionFromBidiLevel(frameLevel); |
michael@0 | 1059 | } |
michael@0 | 1060 | |
michael@0 | 1061 | return writingMode; |
michael@0 | 1062 | } |
michael@0 | 1063 | |
michael@0 | 1064 | nsRect |
michael@0 | 1065 | nsIFrame::GetMarginRectRelativeToSelf() const |
michael@0 | 1066 | { |
michael@0 | 1067 | nsMargin m = GetUsedMargin(); |
michael@0 | 1068 | ApplySkipSides(m); |
michael@0 | 1069 | nsRect r(0, 0, mRect.width, mRect.height); |
michael@0 | 1070 | r.Inflate(m); |
michael@0 | 1071 | return r; |
michael@0 | 1072 | } |
michael@0 | 1073 | |
michael@0 | 1074 | bool |
michael@0 | 1075 | nsIFrame::IsTransformed() const |
michael@0 | 1076 | { |
michael@0 | 1077 | return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && |
michael@0 | 1078 | (StyleDisplay()->HasTransform(this) || |
michael@0 | 1079 | IsSVGTransformed() || |
michael@0 | 1080 | (mContent && |
michael@0 | 1081 | nsLayoutUtils::HasAnimationsForCompositor(mContent, |
michael@0 | 1082 | eCSSProperty_transform) && |
michael@0 | 1083 | IsFrameOfType(eSupportsCSSTransforms) && |
michael@0 | 1084 | mContent->GetPrimaryFrame() == this))); |
michael@0 | 1085 | } |
michael@0 | 1086 | |
michael@0 | 1087 | bool |
michael@0 | 1088 | nsIFrame::HasOpacityInternal(float aThreshold) const |
michael@0 | 1089 | { |
michael@0 | 1090 | MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument"); |
michael@0 | 1091 | const nsStyleDisplay* displayStyle = StyleDisplay(); |
michael@0 | 1092 | return StyleDisplay()->mOpacity < aThreshold || |
michael@0 | 1093 | (displayStyle->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) || |
michael@0 | 1094 | (mContent && |
michael@0 | 1095 | nsLayoutUtils::HasAnimationsForCompositor(mContent, |
michael@0 | 1096 | eCSSProperty_opacity) && |
michael@0 | 1097 | mContent->GetPrimaryFrame() == this); |
michael@0 | 1098 | } |
michael@0 | 1099 | |
michael@0 | 1100 | bool |
michael@0 | 1101 | nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms, |
michael@0 | 1102 | gfx::Matrix *aFromParentTransforms) const |
michael@0 | 1103 | { |
michael@0 | 1104 | return false; |
michael@0 | 1105 | } |
michael@0 | 1106 | |
michael@0 | 1107 | bool |
michael@0 | 1108 | nsIFrame::Preserves3DChildren() const |
michael@0 | 1109 | { |
michael@0 | 1110 | const nsStyleDisplay* disp = StyleDisplay(); |
michael@0 | 1111 | if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D || |
michael@0 | 1112 | !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) { |
michael@0 | 1113 | return false; |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d. |
michael@0 | 1117 | if (GetType() == nsGkAtoms::scrollFrame) { |
michael@0 | 1118 | return false; |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | nsRect temp; |
michael@0 | 1122 | return !nsFrame::ShouldApplyOverflowClipping(this, disp) && |
michael@0 | 1123 | !GetClipPropClipRect(disp, &temp, GetSize()) && |
michael@0 | 1124 | !nsSVGIntegrationUtils::UsingEffectsForFrame(this); |
michael@0 | 1125 | } |
michael@0 | 1126 | |
michael@0 | 1127 | bool |
michael@0 | 1128 | nsIFrame::Preserves3D() const |
michael@0 | 1129 | { |
michael@0 | 1130 | if (!GetParent() || !GetParent()->Preserves3DChildren()) { |
michael@0 | 1131 | return false; |
michael@0 | 1132 | } |
michael@0 | 1133 | return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden(); |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | bool |
michael@0 | 1137 | nsIFrame::HasPerspective() const |
michael@0 | 1138 | { |
michael@0 | 1139 | if (!IsTransformed()) { |
michael@0 | 1140 | return false; |
michael@0 | 1141 | } |
michael@0 | 1142 | nsStyleContext* parentStyleContext = StyleContext()->GetParent(); |
michael@0 | 1143 | if (!parentStyleContext) { |
michael@0 | 1144 | return false; |
michael@0 | 1145 | } |
michael@0 | 1146 | const nsStyleDisplay* parentDisp = parentStyleContext->StyleDisplay(); |
michael@0 | 1147 | return parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord; |
michael@0 | 1148 | } |
michael@0 | 1149 | |
michael@0 | 1150 | bool |
michael@0 | 1151 | nsIFrame::ChildrenHavePerspective() const |
michael@0 | 1152 | { |
michael@0 | 1153 | return StyleDisplay()->HasPerspectiveStyle(); |
michael@0 | 1154 | } |
michael@0 | 1155 | |
michael@0 | 1156 | nsRect |
michael@0 | 1157 | nsIFrame::GetContentRectRelativeToSelf() const |
michael@0 | 1158 | { |
michael@0 | 1159 | nsMargin bp(GetUsedBorderAndPadding()); |
michael@0 | 1160 | ApplySkipSides(bp); |
michael@0 | 1161 | nsRect r(0, 0, mRect.width, mRect.height); |
michael@0 | 1162 | r.Deflate(bp); |
michael@0 | 1163 | return r; |
michael@0 | 1164 | } |
michael@0 | 1165 | |
michael@0 | 1166 | nsRect |
michael@0 | 1167 | nsIFrame::GetContentRect() const |
michael@0 | 1168 | { |
michael@0 | 1169 | return GetContentRectRelativeToSelf() + GetPosition(); |
michael@0 | 1170 | } |
michael@0 | 1171 | |
michael@0 | 1172 | bool |
michael@0 | 1173 | nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius, |
michael@0 | 1174 | const nsSize& aFrameSize, |
michael@0 | 1175 | const nsSize& aBorderArea, |
michael@0 | 1176 | int aSkipSides, |
michael@0 | 1177 | nscoord aRadii[8]) |
michael@0 | 1178 | { |
michael@0 | 1179 | // Percentages are relative to whichever side they're on. |
michael@0 | 1180 | NS_FOR_CSS_HALF_CORNERS(i) { |
michael@0 | 1181 | const nsStyleCoord c = aBorderRadius.Get(i); |
michael@0 | 1182 | nscoord axis = |
michael@0 | 1183 | NS_HALF_CORNER_IS_X(i) ? aFrameSize.width : aFrameSize.height; |
michael@0 | 1184 | |
michael@0 | 1185 | if (c.IsCoordPercentCalcUnit()) { |
michael@0 | 1186 | aRadii[i] = nsRuleNode::ComputeCoordPercentCalc(c, axis); |
michael@0 | 1187 | if (aRadii[i] < 0) { |
michael@0 | 1188 | // clamp calc() |
michael@0 | 1189 | aRadii[i] = 0; |
michael@0 | 1190 | } |
michael@0 | 1191 | } else { |
michael@0 | 1192 | NS_NOTREACHED("ComputeBorderRadii: bad unit"); |
michael@0 | 1193 | aRadii[i] = 0; |
michael@0 | 1194 | } |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | if (aSkipSides & (1 << NS_SIDE_TOP)) { |
michael@0 | 1198 | aRadii[NS_CORNER_TOP_LEFT_X] = 0; |
michael@0 | 1199 | aRadii[NS_CORNER_TOP_LEFT_Y] = 0; |
michael@0 | 1200 | aRadii[NS_CORNER_TOP_RIGHT_X] = 0; |
michael@0 | 1201 | aRadii[NS_CORNER_TOP_RIGHT_Y] = 0; |
michael@0 | 1202 | } |
michael@0 | 1203 | |
michael@0 | 1204 | if (aSkipSides & (1 << NS_SIDE_RIGHT)) { |
michael@0 | 1205 | aRadii[NS_CORNER_TOP_RIGHT_X] = 0; |
michael@0 | 1206 | aRadii[NS_CORNER_TOP_RIGHT_Y] = 0; |
michael@0 | 1207 | aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0; |
michael@0 | 1208 | aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0; |
michael@0 | 1209 | } |
michael@0 | 1210 | |
michael@0 | 1211 | if (aSkipSides & (1 << NS_SIDE_BOTTOM)) { |
michael@0 | 1212 | aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0; |
michael@0 | 1213 | aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0; |
michael@0 | 1214 | aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0; |
michael@0 | 1215 | aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0; |
michael@0 | 1216 | } |
michael@0 | 1217 | |
michael@0 | 1218 | if (aSkipSides & (1 << NS_SIDE_LEFT)) { |
michael@0 | 1219 | aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0; |
michael@0 | 1220 | aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0; |
michael@0 | 1221 | aRadii[NS_CORNER_TOP_LEFT_X] = 0; |
michael@0 | 1222 | aRadii[NS_CORNER_TOP_LEFT_Y] = 0; |
michael@0 | 1223 | } |
michael@0 | 1224 | |
michael@0 | 1225 | // css3-background specifies this algorithm for reducing |
michael@0 | 1226 | // corner radii when they are too big. |
michael@0 | 1227 | bool haveRadius = false; |
michael@0 | 1228 | double ratio = 1.0f; |
michael@0 | 1229 | NS_FOR_CSS_SIDES(side) { |
michael@0 | 1230 | uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, true); |
michael@0 | 1231 | uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, true); |
michael@0 | 1232 | nscoord length = |
michael@0 | 1233 | NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width; |
michael@0 | 1234 | nscoord sum = aRadii[hc1] + aRadii[hc2]; |
michael@0 | 1235 | if (sum) |
michael@0 | 1236 | haveRadius = true; |
michael@0 | 1237 | |
michael@0 | 1238 | // avoid floating point division in the normal case |
michael@0 | 1239 | if (length < sum) |
michael@0 | 1240 | ratio = std::min(ratio, double(length)/sum); |
michael@0 | 1241 | } |
michael@0 | 1242 | if (ratio < 1.0) { |
michael@0 | 1243 | NS_FOR_CSS_HALF_CORNERS(corner) { |
michael@0 | 1244 | aRadii[corner] *= ratio; |
michael@0 | 1245 | } |
michael@0 | 1246 | } |
michael@0 | 1247 | |
michael@0 | 1248 | return haveRadius; |
michael@0 | 1249 | } |
michael@0 | 1250 | |
michael@0 | 1251 | /* static */ void |
michael@0 | 1252 | nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) |
michael@0 | 1253 | { |
michael@0 | 1254 | NS_FOR_CSS_SIDES(side) { |
michael@0 | 1255 | nscoord offset = aOffsets.Side(side); |
michael@0 | 1256 | uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false); |
michael@0 | 1257 | uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false); |
michael@0 | 1258 | aRadii[hc1] = std::max(0, aRadii[hc1] - offset); |
michael@0 | 1259 | aRadii[hc2] = std::max(0, aRadii[hc2] - offset); |
michael@0 | 1260 | } |
michael@0 | 1261 | } |
michael@0 | 1262 | |
michael@0 | 1263 | /* static */ void |
michael@0 | 1264 | nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) |
michael@0 | 1265 | { |
michael@0 | 1266 | NS_FOR_CSS_SIDES(side) { |
michael@0 | 1267 | nscoord offset = aOffsets.Side(side); |
michael@0 | 1268 | uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false); |
michael@0 | 1269 | uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false); |
michael@0 | 1270 | if (aRadii[hc1] > 0) |
michael@0 | 1271 | aRadii[hc1] += offset; |
michael@0 | 1272 | if (aRadii[hc2] > 0) |
michael@0 | 1273 | aRadii[hc2] += offset; |
michael@0 | 1274 | } |
michael@0 | 1275 | } |
michael@0 | 1276 | |
michael@0 | 1277 | /* virtual */ bool |
michael@0 | 1278 | nsIFrame::GetBorderRadii(nscoord aRadii[8]) const |
michael@0 | 1279 | { |
michael@0 | 1280 | if (IsThemed()) { |
michael@0 | 1281 | // When we're themed, the native theme code draws the border and |
michael@0 | 1282 | // background, and therefore it doesn't make sense to tell other |
michael@0 | 1283 | // code that's interested in border-radius that we have any radii. |
michael@0 | 1284 | // |
michael@0 | 1285 | // In an ideal world, we might have a way for the them to tell us an |
michael@0 | 1286 | // border radius, but since we don't, we're better off assuming |
michael@0 | 1287 | // zero. |
michael@0 | 1288 | NS_FOR_CSS_HALF_CORNERS(corner) { |
michael@0 | 1289 | aRadii[corner] = 0; |
michael@0 | 1290 | } |
michael@0 | 1291 | return false; |
michael@0 | 1292 | } |
michael@0 | 1293 | nsSize size = GetSize(); |
michael@0 | 1294 | return ComputeBorderRadii(StyleBorder()->mBorderRadius, size, size, |
michael@0 | 1295 | GetSkipSides(), aRadii); |
michael@0 | 1296 | } |
michael@0 | 1297 | |
michael@0 | 1298 | bool |
michael@0 | 1299 | nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const |
michael@0 | 1300 | { |
michael@0 | 1301 | if (!GetBorderRadii(aRadii)) |
michael@0 | 1302 | return false; |
michael@0 | 1303 | InsetBorderRadii(aRadii, GetUsedBorder()); |
michael@0 | 1304 | NS_FOR_CSS_HALF_CORNERS(corner) { |
michael@0 | 1305 | if (aRadii[corner]) |
michael@0 | 1306 | return true; |
michael@0 | 1307 | } |
michael@0 | 1308 | return false; |
michael@0 | 1309 | } |
michael@0 | 1310 | |
michael@0 | 1311 | bool |
michael@0 | 1312 | nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const |
michael@0 | 1313 | { |
michael@0 | 1314 | if (!GetBorderRadii(aRadii)) |
michael@0 | 1315 | return false; |
michael@0 | 1316 | InsetBorderRadii(aRadii, GetUsedBorderAndPadding()); |
michael@0 | 1317 | NS_FOR_CSS_HALF_CORNERS(corner) { |
michael@0 | 1318 | if (aRadii[corner]) |
michael@0 | 1319 | return true; |
michael@0 | 1320 | } |
michael@0 | 1321 | return false; |
michael@0 | 1322 | } |
michael@0 | 1323 | |
michael@0 | 1324 | nsStyleContext* |
michael@0 | 1325 | nsFrame::GetAdditionalStyleContext(int32_t aIndex) const |
michael@0 | 1326 | { |
michael@0 | 1327 | NS_PRECONDITION(aIndex >= 0, "invalid index number"); |
michael@0 | 1328 | return nullptr; |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | void |
michael@0 | 1332 | nsFrame::SetAdditionalStyleContext(int32_t aIndex, |
michael@0 | 1333 | nsStyleContext* aStyleContext) |
michael@0 | 1334 | { |
michael@0 | 1335 | NS_PRECONDITION(aIndex >= 0, "invalid index number"); |
michael@0 | 1336 | } |
michael@0 | 1337 | |
michael@0 | 1338 | nscoord |
michael@0 | 1339 | nsFrame::GetBaseline() const |
michael@0 | 1340 | { |
michael@0 | 1341 | NS_ASSERTION(!NS_SUBTREE_DIRTY(this), |
michael@0 | 1342 | "frame must not be dirty"); |
michael@0 | 1343 | // Default to the bottom margin edge, per CSS2.1's definition of the |
michael@0 | 1344 | // 'baseline' value of 'vertical-align'. |
michael@0 | 1345 | return mRect.height + GetUsedMargin().bottom; |
michael@0 | 1346 | } |
michael@0 | 1347 | |
michael@0 | 1348 | const nsFrameList& |
michael@0 | 1349 | nsFrame::GetChildList(ChildListID aListID) const |
michael@0 | 1350 | { |
michael@0 | 1351 | if (IsAbsoluteContainer() && |
michael@0 | 1352 | aListID == GetAbsoluteListID()) { |
michael@0 | 1353 | return GetAbsoluteContainingBlock()->GetChildList(); |
michael@0 | 1354 | } else { |
michael@0 | 1355 | return nsFrameList::EmptyList(); |
michael@0 | 1356 | } |
michael@0 | 1357 | } |
michael@0 | 1358 | |
michael@0 | 1359 | void |
michael@0 | 1360 | nsFrame::GetChildLists(nsTArray<ChildList>* aLists) const |
michael@0 | 1361 | { |
michael@0 | 1362 | if (IsAbsoluteContainer()) { |
michael@0 | 1363 | nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList(); |
michael@0 | 1364 | absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID()); |
michael@0 | 1365 | } |
michael@0 | 1366 | } |
michael@0 | 1367 | |
michael@0 | 1368 | void |
michael@0 | 1369 | nsIFrame::GetCrossDocChildLists(nsTArray<ChildList>* aLists) |
michael@0 | 1370 | { |
michael@0 | 1371 | nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this); |
michael@0 | 1372 | if (subdocumentFrame) { |
michael@0 | 1373 | // Descend into the subdocument |
michael@0 | 1374 | nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); |
michael@0 | 1375 | if (root) { |
michael@0 | 1376 | aLists->AppendElement(nsIFrame::ChildList( |
michael@0 | 1377 | nsFrameList(root, nsLayoutUtils::GetLastSibling(root)), |
michael@0 | 1378 | nsIFrame::kPrincipalList)); |
michael@0 | 1379 | } |
michael@0 | 1380 | } |
michael@0 | 1381 | |
michael@0 | 1382 | GetChildLists(aLists); |
michael@0 | 1383 | } |
michael@0 | 1384 | |
michael@0 | 1385 | static nsIFrame* |
michael@0 | 1386 | GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame) |
michael@0 | 1387 | { |
michael@0 | 1388 | nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); |
michael@0 | 1389 | if (capturingContent) { |
michael@0 | 1390 | nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent); |
michael@0 | 1391 | return activeFrame ? activeFrame : aFrame; |
michael@0 | 1392 | } |
michael@0 | 1393 | |
michael@0 | 1394 | return aFrame; |
michael@0 | 1395 | } |
michael@0 | 1396 | |
michael@0 | 1397 | int16_t |
michael@0 | 1398 | nsFrame::DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn) |
michael@0 | 1399 | { |
michael@0 | 1400 | int16_t selType = nsISelectionController::SELECTION_OFF; |
michael@0 | 1401 | |
michael@0 | 1402 | nsCOMPtr<nsISelectionController> selCon; |
michael@0 | 1403 | nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon)); |
michael@0 | 1404 | if (NS_SUCCEEDED(result) && selCon) { |
michael@0 | 1405 | result = selCon->GetDisplaySelection(&selType); |
michael@0 | 1406 | if (NS_SUCCEEDED(result) && (selType != nsISelectionController::SELECTION_OFF)) { |
michael@0 | 1407 | // Check whether style allows selection. |
michael@0 | 1408 | bool selectable; |
michael@0 | 1409 | IsSelectable(&selectable, nullptr); |
michael@0 | 1410 | if (!selectable) { |
michael@0 | 1411 | selType = nsISelectionController::SELECTION_OFF; |
michael@0 | 1412 | isOkToTurnOn = false; |
michael@0 | 1413 | } |
michael@0 | 1414 | } |
michael@0 | 1415 | if (isOkToTurnOn && (selType == nsISelectionController::SELECTION_OFF)) { |
michael@0 | 1416 | selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); |
michael@0 | 1417 | selType = nsISelectionController::SELECTION_ON; |
michael@0 | 1418 | } |
michael@0 | 1419 | } |
michael@0 | 1420 | return selType; |
michael@0 | 1421 | } |
michael@0 | 1422 | |
michael@0 | 1423 | class nsDisplaySelectionOverlay : public nsDisplayItem { |
michael@0 | 1424 | public: |
michael@0 | 1425 | nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, |
michael@0 | 1426 | nsFrame* aFrame, int16_t aSelectionValue) |
michael@0 | 1427 | : nsDisplayItem(aBuilder, aFrame), mSelectionValue(aSelectionValue) { |
michael@0 | 1428 | MOZ_COUNT_CTOR(nsDisplaySelectionOverlay); |
michael@0 | 1429 | } |
michael@0 | 1430 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 1431 | virtual ~nsDisplaySelectionOverlay() { |
michael@0 | 1432 | MOZ_COUNT_DTOR(nsDisplaySelectionOverlay); |
michael@0 | 1433 | } |
michael@0 | 1434 | #endif |
michael@0 | 1435 | |
michael@0 | 1436 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1437 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 1438 | NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY) |
michael@0 | 1439 | private: |
michael@0 | 1440 | int16_t mSelectionValue; |
michael@0 | 1441 | }; |
michael@0 | 1442 | |
michael@0 | 1443 | void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1444 | nsRenderingContext* aCtx) |
michael@0 | 1445 | { |
michael@0 | 1446 | LookAndFeel::ColorID colorID; |
michael@0 | 1447 | if (mSelectionValue == nsISelectionController::SELECTION_ON) { |
michael@0 | 1448 | colorID = LookAndFeel::eColorID_TextSelectBackground; |
michael@0 | 1449 | } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) { |
michael@0 | 1450 | colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention; |
michael@0 | 1451 | } else { |
michael@0 | 1452 | colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled; |
michael@0 | 1453 | } |
michael@0 | 1454 | |
michael@0 | 1455 | nscolor color = LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)); |
michael@0 | 1456 | |
michael@0 | 1457 | gfxRGBA c(color); |
michael@0 | 1458 | c.a = .5; |
michael@0 | 1459 | |
michael@0 | 1460 | gfxContext *ctx = aCtx->ThebesContext(); |
michael@0 | 1461 | ctx->SetColor(c); |
michael@0 | 1462 | |
michael@0 | 1463 | nsIntRect pxRect = |
michael@0 | 1464 | mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel()); |
michael@0 | 1465 | ctx->NewPath(); |
michael@0 | 1466 | ctx->Rectangle(gfxRect(pxRect.x, pxRect.y, pxRect.width, pxRect.height), true); |
michael@0 | 1467 | ctx->Fill(); |
michael@0 | 1468 | } |
michael@0 | 1469 | |
michael@0 | 1470 | /******************************************************** |
michael@0 | 1471 | * Refreshes each content's frame |
michael@0 | 1472 | *********************************************************/ |
michael@0 | 1473 | |
michael@0 | 1474 | void |
michael@0 | 1475 | nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, |
michael@0 | 1476 | nsDisplayList* aList, |
michael@0 | 1477 | uint16_t aContentType) |
michael@0 | 1478 | { |
michael@0 | 1479 | if (!IsSelected() || !IsVisibleForPainting(aBuilder)) |
michael@0 | 1480 | return; |
michael@0 | 1481 | |
michael@0 | 1482 | nsPresContext* presContext = PresContext(); |
michael@0 | 1483 | nsIPresShell *shell = presContext->PresShell(); |
michael@0 | 1484 | if (!shell) |
michael@0 | 1485 | return; |
michael@0 | 1486 | |
michael@0 | 1487 | int16_t displaySelection = shell->GetSelectionFlags(); |
michael@0 | 1488 | if (!(displaySelection & aContentType)) |
michael@0 | 1489 | return; |
michael@0 | 1490 | |
michael@0 | 1491 | const nsFrameSelection* frameSelection = GetConstFrameSelection(); |
michael@0 | 1492 | int16_t selectionValue = frameSelection->GetDisplaySelection(); |
michael@0 | 1493 | |
michael@0 | 1494 | if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) |
michael@0 | 1495 | return; // selection is hidden or off |
michael@0 | 1496 | |
michael@0 | 1497 | nsIContent *newContent = mContent->GetParent(); |
michael@0 | 1498 | |
michael@0 | 1499 | //check to see if we are anonymous content |
michael@0 | 1500 | int32_t offset = 0; |
michael@0 | 1501 | if (newContent) { |
michael@0 | 1502 | // XXXbz there has GOT to be a better way of determining this! |
michael@0 | 1503 | offset = newContent->IndexOf(mContent); |
michael@0 | 1504 | } |
michael@0 | 1505 | |
michael@0 | 1506 | SelectionDetails *details; |
michael@0 | 1507 | //look up to see what selection(s) are on this frame |
michael@0 | 1508 | details = frameSelection->LookUpSelection(newContent, offset, 1, false); |
michael@0 | 1509 | if (!details) |
michael@0 | 1510 | return; |
michael@0 | 1511 | |
michael@0 | 1512 | bool normal = false; |
michael@0 | 1513 | while (details) { |
michael@0 | 1514 | if (details->mType == nsISelectionController::SELECTION_NORMAL) { |
michael@0 | 1515 | normal = true; |
michael@0 | 1516 | } |
michael@0 | 1517 | SelectionDetails *next = details->mNext; |
michael@0 | 1518 | delete details; |
michael@0 | 1519 | details = next; |
michael@0 | 1520 | } |
michael@0 | 1521 | |
michael@0 | 1522 | if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) { |
michael@0 | 1523 | // Don't overlay an image if it's not in the primary selection. |
michael@0 | 1524 | return; |
michael@0 | 1525 | } |
michael@0 | 1526 | |
michael@0 | 1527 | aList->AppendNewToTop(new (aBuilder) |
michael@0 | 1528 | nsDisplaySelectionOverlay(aBuilder, this, selectionValue)); |
michael@0 | 1529 | } |
michael@0 | 1530 | |
michael@0 | 1531 | void |
michael@0 | 1532 | nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder, |
michael@0 | 1533 | const nsDisplayListSet& aLists) |
michael@0 | 1534 | { |
michael@0 | 1535 | if (StyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) |
michael@0 | 1536 | return; |
michael@0 | 1537 | |
michael@0 | 1538 | aLists.Outlines()->AppendNewToTop( |
michael@0 | 1539 | new (aBuilder) nsDisplayOutline(aBuilder, this)); |
michael@0 | 1540 | } |
michael@0 | 1541 | |
michael@0 | 1542 | void |
michael@0 | 1543 | nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder, |
michael@0 | 1544 | const nsDisplayListSet& aLists) |
michael@0 | 1545 | { |
michael@0 | 1546 | if (!IsVisibleForPainting(aBuilder)) |
michael@0 | 1547 | return; |
michael@0 | 1548 | |
michael@0 | 1549 | DisplayOutlineUnconditional(aBuilder, aLists); |
michael@0 | 1550 | } |
michael@0 | 1551 | |
michael@0 | 1552 | void |
michael@0 | 1553 | nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder, |
michael@0 | 1554 | const nsRect& aDirtyRect, nsDisplayList* aList) |
michael@0 | 1555 | { |
michael@0 | 1556 | if (!IsVisibleForPainting(aBuilder)) |
michael@0 | 1557 | return; |
michael@0 | 1558 | |
michael@0 | 1559 | aList->AppendNewToTop( |
michael@0 | 1560 | new (aBuilder) nsDisplayCaret(aBuilder, this, aBuilder->GetCaret())); |
michael@0 | 1561 | } |
michael@0 | 1562 | |
michael@0 | 1563 | nscolor |
michael@0 | 1564 | nsIFrame::GetCaretColorAt(int32_t aOffset) |
michael@0 | 1565 | { |
michael@0 | 1566 | nscolor color = NS_RGB(0, 0, 0); |
michael@0 | 1567 | if (nsLayoutUtils::GetNativeTextColor(this, color)) |
michael@0 | 1568 | return color; |
michael@0 | 1569 | |
michael@0 | 1570 | // Use CSS text color. |
michael@0 | 1571 | return StyleColor()->mColor; |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | bool |
michael@0 | 1575 | nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder, |
michael@0 | 1576 | const nsDisplayListSet& aLists, |
michael@0 | 1577 | bool aForceBackground) |
michael@0 | 1578 | { |
michael@0 | 1579 | // Here we don't try to detect background propagation. Frames that might |
michael@0 | 1580 | // receive a propagated background should just set aForceBackground to |
michael@0 | 1581 | // true. |
michael@0 | 1582 | if (aBuilder->IsForEventDelivery() || aForceBackground || |
michael@0 | 1583 | !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) { |
michael@0 | 1584 | return nsDisplayBackgroundImage::AppendBackgroundItemsToTop( |
michael@0 | 1585 | aBuilder, this, aLists.BorderBackground()); |
michael@0 | 1586 | } |
michael@0 | 1587 | return false; |
michael@0 | 1588 | } |
michael@0 | 1589 | |
michael@0 | 1590 | void |
michael@0 | 1591 | nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, |
michael@0 | 1592 | const nsDisplayListSet& aLists, |
michael@0 | 1593 | bool aForceBackground) |
michael@0 | 1594 | { |
michael@0 | 1595 | // The visibility check belongs here since child elements have the |
michael@0 | 1596 | // opportunity to override the visibility property and display even if |
michael@0 | 1597 | // their parent is hidden. |
michael@0 | 1598 | if (!IsVisibleForPainting(aBuilder)) |
michael@0 | 1599 | return; |
michael@0 | 1600 | |
michael@0 | 1601 | nsCSSShadowArray* shadows = StyleBorder()->mBoxShadow; |
michael@0 | 1602 | if (shadows && shadows->HasShadowWithInset(false)) { |
michael@0 | 1603 | aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
michael@0 | 1604 | nsDisplayBoxShadowOuter(aBuilder, this)); |
michael@0 | 1605 | } |
michael@0 | 1606 | |
michael@0 | 1607 | bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists, |
michael@0 | 1608 | aForceBackground); |
michael@0 | 1609 | |
michael@0 | 1610 | if (shadows && shadows->HasShadowWithInset(true)) { |
michael@0 | 1611 | aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
michael@0 | 1612 | nsDisplayBoxShadowInner(aBuilder, this)); |
michael@0 | 1613 | } |
michael@0 | 1614 | |
michael@0 | 1615 | // If there's a themed background, we should not create a border item. |
michael@0 | 1616 | // It won't be rendered. |
michael@0 | 1617 | if (!bgIsThemed && StyleBorder()->HasBorder()) { |
michael@0 | 1618 | aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
michael@0 | 1619 | nsDisplayBorder(aBuilder, this)); |
michael@0 | 1620 | } |
michael@0 | 1621 | |
michael@0 | 1622 | DisplayOutlineUnconditional(aBuilder, aLists); |
michael@0 | 1623 | } |
michael@0 | 1624 | |
michael@0 | 1625 | inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame) |
michael@0 | 1626 | { |
michael@0 | 1627 | // The CSS spec says that the 'clip' property only applies to absolutely |
michael@0 | 1628 | // positioned elements, whereas the SVG spec says that it applies to SVG |
michael@0 | 1629 | // elements regardless of the value of the 'position' property. Here we obey |
michael@0 | 1630 | // the CSS spec for outer-<svg> (since that's what we generally do), but |
michael@0 | 1631 | // obey the SVG spec for other SVG elements to which 'clip' applies. |
michael@0 | 1632 | nsIAtom *tag = aFrame->GetContent()->Tag(); |
michael@0 | 1633 | return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && |
michael@0 | 1634 | (tag == nsGkAtoms::svg || tag == nsGkAtoms::foreignObject); |
michael@0 | 1635 | } |
michael@0 | 1636 | |
michael@0 | 1637 | bool |
michael@0 | 1638 | nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, |
michael@0 | 1639 | const nsSize& aSize) const |
michael@0 | 1640 | { |
michael@0 | 1641 | NS_PRECONDITION(aRect, "Must have aRect out parameter"); |
michael@0 | 1642 | |
michael@0 | 1643 | if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) || |
michael@0 | 1644 | !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) { |
michael@0 | 1645 | return false; |
michael@0 | 1646 | } |
michael@0 | 1647 | |
michael@0 | 1648 | *aRect = aDisp->mClip; |
michael@0 | 1649 | if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) { |
michael@0 | 1650 | aRect->width = aSize.width - aRect->x; |
michael@0 | 1651 | } |
michael@0 | 1652 | if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) { |
michael@0 | 1653 | aRect->height = aSize.height - aRect->y; |
michael@0 | 1654 | } |
michael@0 | 1655 | return true; |
michael@0 | 1656 | } |
michael@0 | 1657 | |
michael@0 | 1658 | /** |
michael@0 | 1659 | * If the CSS 'clip' property applies to this frame, set it up |
michael@0 | 1660 | * in aBuilder->ClipState() to clip all content descendants. Returns true |
michael@0 | 1661 | * if the property applies, and if so also returns the clip rect (relative |
michael@0 | 1662 | * to aFrame) in *aRect. |
michael@0 | 1663 | */ |
michael@0 | 1664 | static bool |
michael@0 | 1665 | ApplyClipPropClipping(nsDisplayListBuilder* aBuilder, |
michael@0 | 1666 | const nsIFrame* aFrame, |
michael@0 | 1667 | const nsStyleDisplay* aDisp, |
michael@0 | 1668 | nsRect* aRect, |
michael@0 | 1669 | DisplayListClipState::AutoSaveRestore& aClipState) |
michael@0 | 1670 | { |
michael@0 | 1671 | if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize())) |
michael@0 | 1672 | return false; |
michael@0 | 1673 | |
michael@0 | 1674 | nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame); |
michael@0 | 1675 | aClipState.ClipContentDescendants(clipRect); |
michael@0 | 1676 | return true; |
michael@0 | 1677 | } |
michael@0 | 1678 | |
michael@0 | 1679 | /** |
michael@0 | 1680 | * If the CSS 'overflow' property applies to this frame, and is not |
michael@0 | 1681 | * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping |
michael@0 | 1682 | * for that overflow in aBuilder->ClipState() to clip all containing-block |
michael@0 | 1683 | * descendants. |
michael@0 | 1684 | */ |
michael@0 | 1685 | static void |
michael@0 | 1686 | ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, |
michael@0 | 1687 | const nsIFrame* aFrame, |
michael@0 | 1688 | const nsStyleDisplay* aDisp, |
michael@0 | 1689 | DisplayListClipState::AutoClipMultiple& aClipState) |
michael@0 | 1690 | { |
michael@0 | 1691 | // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table |
michael@0 | 1692 | // frames, and any non-visible value for blocks in a paginated context). |
michael@0 | 1693 | // We allow -moz-hidden-unscrollable to apply to any kind of frame. This |
michael@0 | 1694 | // is required by comboboxes which make their display text (an inline frame) |
michael@0 | 1695 | // have clipping. |
michael@0 | 1696 | if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) { |
michael@0 | 1697 | return; |
michael@0 | 1698 | } |
michael@0 | 1699 | nsRect clipRect; |
michael@0 | 1700 | bool haveRadii = false; |
michael@0 | 1701 | nscoord radii[8]; |
michael@0 | 1702 | if (aFrame->StyleDisplay()->mOverflowClipBox == |
michael@0 | 1703 | NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX) { |
michael@0 | 1704 | clipRect = aFrame->GetPaddingRectRelativeToSelf() + |
michael@0 | 1705 | aBuilder->ToReferenceFrame(aFrame); |
michael@0 | 1706 | haveRadii = aFrame->GetPaddingBoxBorderRadii(radii); |
michael@0 | 1707 | } else { |
michael@0 | 1708 | clipRect = aFrame->GetContentRectRelativeToSelf() + |
michael@0 | 1709 | aBuilder->ToReferenceFrame(aFrame); |
michael@0 | 1710 | // XXX border-radius |
michael@0 | 1711 | } |
michael@0 | 1712 | aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr); |
michael@0 | 1713 | } |
michael@0 | 1714 | |
michael@0 | 1715 | #ifdef DEBUG |
michael@0 | 1716 | static void PaintDebugBorder(nsIFrame* aFrame, nsRenderingContext* aCtx, |
michael@0 | 1717 | const nsRect& aDirtyRect, nsPoint aPt) { |
michael@0 | 1718 | nsRect r(aPt, aFrame->GetSize()); |
michael@0 | 1719 | if (aFrame->HasView()) { |
michael@0 | 1720 | aCtx->SetColor(NS_RGB(0,0,255)); |
michael@0 | 1721 | } else { |
michael@0 | 1722 | aCtx->SetColor(NS_RGB(255,0,0)); |
michael@0 | 1723 | } |
michael@0 | 1724 | aCtx->DrawRect(r); |
michael@0 | 1725 | } |
michael@0 | 1726 | |
michael@0 | 1727 | static void PaintEventTargetBorder(nsIFrame* aFrame, nsRenderingContext* aCtx, |
michael@0 | 1728 | const nsRect& aDirtyRect, nsPoint aPt) { |
michael@0 | 1729 | nsRect r(aPt, aFrame->GetSize()); |
michael@0 | 1730 | aCtx->SetColor(NS_RGB(128,0,128)); |
michael@0 | 1731 | aCtx->DrawRect(r); |
michael@0 | 1732 | } |
michael@0 | 1733 | |
michael@0 | 1734 | static void |
michael@0 | 1735 | DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, |
michael@0 | 1736 | const nsDisplayListSet& aLists) { |
michael@0 | 1737 | // Draw a border around the child |
michael@0 | 1738 | // REVIEW: From nsContainerFrame::PaintChild |
michael@0 | 1739 | if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) { |
michael@0 | 1740 | aLists.Outlines()->AppendNewToTop(new (aBuilder) |
michael@0 | 1741 | nsDisplayGeneric(aBuilder, aFrame, PaintDebugBorder, "DebugBorder", |
michael@0 | 1742 | nsDisplayItem::TYPE_DEBUG_BORDER)); |
michael@0 | 1743 | } |
michael@0 | 1744 | // Draw a border around the current event target |
michael@0 | 1745 | if (nsFrame::GetShowEventTargetFrameBorder() && |
michael@0 | 1746 | aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) { |
michael@0 | 1747 | aLists.Outlines()->AppendNewToTop(new (aBuilder) |
michael@0 | 1748 | nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder", |
michael@0 | 1749 | nsDisplayItem::TYPE_EVENT_TARGET_BORDER)); |
michael@0 | 1750 | } |
michael@0 | 1751 | } |
michael@0 | 1752 | #endif |
michael@0 | 1753 | |
michael@0 | 1754 | static nsresult |
michael@0 | 1755 | WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsDisplayList *aList, nsDisplayList *aOutput, uint32_t& aIndex, nsDisplayList* aTemp) |
michael@0 | 1756 | { |
michael@0 | 1757 | if (aIndex > nsDisplayTransform::INDEX_MAX) { |
michael@0 | 1758 | return NS_OK; |
michael@0 | 1759 | } |
michael@0 | 1760 | |
michael@0 | 1761 | nsresult rv = NS_OK; |
michael@0 | 1762 | while (nsDisplayItem *item = aList->RemoveBottom()) { |
michael@0 | 1763 | nsIFrame *childFrame = item->Frame(); |
michael@0 | 1764 | |
michael@0 | 1765 | // We accumulate sequential items that aren't transforms into the 'temp' list |
michael@0 | 1766 | // and then flush this list into aOutput by wrapping the whole lot with a single |
michael@0 | 1767 | // nsDisplayTransform. |
michael@0 | 1768 | |
michael@0 | 1769 | if (childFrame->GetParent() && |
michael@0 | 1770 | (childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) { |
michael@0 | 1771 | switch (item->GetType()) { |
michael@0 | 1772 | case nsDisplayItem::TYPE_TRANSFORM: { |
michael@0 | 1773 | if (!aTemp->IsEmpty()) { |
michael@0 | 1774 | aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
michael@0 | 1775 | } |
michael@0 | 1776 | // Override item's clipping with our current clip state (if any). Since we're |
michael@0 | 1777 | // bubbling up a preserve-3d transformed child to a preserve-3d parent, |
michael@0 | 1778 | // we can be sure the child doesn't have clip state of its own. |
michael@0 | 1779 | NS_ASSERTION(!item->GetClip().HasClip(), "Unexpected clip on item"); |
michael@0 | 1780 | const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder); |
michael@0 | 1781 | if (clip) { |
michael@0 | 1782 | item->SetClip(aBuilder, *clip); |
michael@0 | 1783 | } |
michael@0 | 1784 | aOutput->AppendToTop(item); |
michael@0 | 1785 | break; |
michael@0 | 1786 | } |
michael@0 | 1787 | case nsDisplayItem::TYPE_WRAP_LIST: { |
michael@0 | 1788 | nsDisplayWrapList *list = static_cast<nsDisplayWrapList*>(item); |
michael@0 | 1789 | rv = WrapPreserve3DListInternal(aFrame, aBuilder, |
michael@0 | 1790 | list->GetChildren(), aOutput, aIndex, aTemp); |
michael@0 | 1791 | list->~nsDisplayWrapList(); |
michael@0 | 1792 | break; |
michael@0 | 1793 | } |
michael@0 | 1794 | case nsDisplayItem::TYPE_OPACITY: { |
michael@0 | 1795 | if (!aTemp->IsEmpty()) { |
michael@0 | 1796 | aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
michael@0 | 1797 | } |
michael@0 | 1798 | nsDisplayOpacity *opacity = static_cast<nsDisplayOpacity*>(item); |
michael@0 | 1799 | nsDisplayList output; |
michael@0 | 1800 | // Call GetChildren, not GetSameCoordinateSystemChildren, because |
michael@0 | 1801 | // the preserve-3d children of 'opacity' are temporarily not in the |
michael@0 | 1802 | // same coordinate system as the opacity --- until this wrapping is done. |
michael@0 | 1803 | rv = WrapPreserve3DListInternal(aFrame, aBuilder, |
michael@0 | 1804 | opacity->GetChildren(), &output, aIndex, aTemp); |
michael@0 | 1805 | if (!aTemp->IsEmpty()) { |
michael@0 | 1806 | output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
michael@0 | 1807 | } |
michael@0 | 1808 | opacity->GetChildren()->AppendToTop(&output); |
michael@0 | 1809 | opacity->UpdateBounds(aBuilder); |
michael@0 | 1810 | aOutput->AppendToTop(item); |
michael@0 | 1811 | break; |
michael@0 | 1812 | } |
michael@0 | 1813 | default: { |
michael@0 | 1814 | if (childFrame->StyleDisplay()->BackfaceIsHidden()) { |
michael@0 | 1815 | if (!aTemp->IsEmpty()) { |
michael@0 | 1816 | aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); |
michael@0 | 1817 | } |
michael@0 | 1818 | |
michael@0 | 1819 | aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, childFrame, item, aIndex++)); |
michael@0 | 1820 | } else { |
michael@0 | 1821 | aTemp->AppendToTop(item); |
michael@0 | 1822 | } |
michael@0 | 1823 | break; |
michael@0 | 1824 | } |
michael@0 | 1825 | } |
michael@0 | 1826 | } else { |
michael@0 | 1827 | aTemp->AppendToTop(item); |
michael@0 | 1828 | } |
michael@0 | 1829 | |
michael@0 | 1830 | if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX) |
michael@0 | 1831 | return rv; |
michael@0 | 1832 | } |
michael@0 | 1833 | |
michael@0 | 1834 | return NS_OK; |
michael@0 | 1835 | } |
michael@0 | 1836 | |
michael@0 | 1837 | static bool |
michael@0 | 1838 | IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame) |
michael@0 | 1839 | { |
michael@0 | 1840 | return aScrollableFrame && aScrollableFrame->IsScrollingActive(); |
michael@0 | 1841 | } |
michael@0 | 1842 | |
michael@0 | 1843 | static nsresult |
michael@0 | 1844 | WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayList *aList) |
michael@0 | 1845 | { |
michael@0 | 1846 | uint32_t index = 0; |
michael@0 | 1847 | nsDisplayList temp; |
michael@0 | 1848 | nsDisplayList output; |
michael@0 | 1849 | nsresult rv = WrapPreserve3DListInternal(aFrame, aBuilder, aList, &output, index, &temp); |
michael@0 | 1850 | |
michael@0 | 1851 | if (!temp.IsEmpty()) { |
michael@0 | 1852 | output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, &temp, index++)); |
michael@0 | 1853 | } |
michael@0 | 1854 | |
michael@0 | 1855 | aList->AppendToTop(&output); |
michael@0 | 1856 | return rv; |
michael@0 | 1857 | } |
michael@0 | 1858 | |
michael@0 | 1859 | class AutoSaveRestoreBlendMode |
michael@0 | 1860 | { |
michael@0 | 1861 | nsDisplayListBuilder& mBuilder; |
michael@0 | 1862 | bool AutoResetContainsBlendMode; |
michael@0 | 1863 | public: |
michael@0 | 1864 | AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder) |
michael@0 | 1865 | : mBuilder(aBuilder), |
michael@0 | 1866 | AutoResetContainsBlendMode(aBuilder.ContainsBlendMode()) { |
michael@0 | 1867 | } |
michael@0 | 1868 | |
michael@0 | 1869 | ~AutoSaveRestoreBlendMode() { |
michael@0 | 1870 | mBuilder.SetContainsBlendMode(AutoResetContainsBlendMode); |
michael@0 | 1871 | } |
michael@0 | 1872 | }; |
michael@0 | 1873 | |
michael@0 | 1874 | static void |
michael@0 | 1875 | CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
michael@0 | 1876 | { |
michael@0 | 1877 | nsIContent* content = aFrame->GetContent(); |
michael@0 | 1878 | if (!content) { |
michael@0 | 1879 | return; |
michael@0 | 1880 | } |
michael@0 | 1881 | EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content); |
michael@0 | 1882 | if (!elm) { |
michael@0 | 1883 | return; |
michael@0 | 1884 | } |
michael@0 | 1885 | if (elm->HasListenersFor(nsGkAtoms::ontouchstart) || |
michael@0 | 1886 | elm->HasListenersFor(nsGkAtoms::ontouchmove)) { |
michael@0 | 1887 | aBuilder->SetAncestorHasTouchEventHandler(true); |
michael@0 | 1888 | } |
michael@0 | 1889 | } |
michael@0 | 1890 | |
michael@0 | 1891 | void |
michael@0 | 1892 | nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, |
michael@0 | 1893 | const nsRect& aDirtyRect, |
michael@0 | 1894 | nsDisplayList* aList) { |
michael@0 | 1895 | if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) |
michael@0 | 1896 | return; |
michael@0 | 1897 | |
michael@0 | 1898 | // Replaced elements have their visibility handled here, because |
michael@0 | 1899 | // they're visually atomic |
michael@0 | 1900 | if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder)) |
michael@0 | 1901 | return; |
michael@0 | 1902 | |
michael@0 | 1903 | const nsStyleDisplay* disp = StyleDisplay(); |
michael@0 | 1904 | // We can stop right away if this is a zero-opacity stacking context and |
michael@0 | 1905 | // we're painting, and we're not animating opacity. Don't do this |
michael@0 | 1906 | // if we're going to compute plugin geometry, since opacity-0 plugins |
michael@0 | 1907 | // need to have display items built for them. |
michael@0 | 1908 | if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && |
michael@0 | 1909 | !aBuilder->WillComputePluginGeometry() && |
michael@0 | 1910 | !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && |
michael@0 | 1911 | !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) { |
michael@0 | 1912 | return; |
michael@0 | 1913 | } |
michael@0 | 1914 | |
michael@0 | 1915 | nsRect dirtyRect = aDirtyRect; |
michael@0 | 1916 | |
michael@0 | 1917 | bool inTransform = aBuilder->IsInTransform(); |
michael@0 | 1918 | bool isTransformed = IsTransformed(); |
michael@0 | 1919 | // reset blend mode so we can keep track if this stacking context needs have |
michael@0 | 1920 | // a nsDisplayBlendContainer. Set the blend mode back when the routine exits |
michael@0 | 1921 | // so we keep track if the parent stacking context needs a container too. |
michael@0 | 1922 | AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder); |
michael@0 | 1923 | aBuilder->SetContainsBlendMode(false); |
michael@0 | 1924 | |
michael@0 | 1925 | if (isTransformed) { |
michael@0 | 1926 | const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); |
michael@0 | 1927 | if (aBuilder->IsForPainting() && |
michael@0 | 1928 | nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) { |
michael@0 | 1929 | dirtyRect = overflow; |
michael@0 | 1930 | } else { |
michael@0 | 1931 | if (overflow.IsEmpty() && !Preserves3DChildren()) { |
michael@0 | 1932 | return; |
michael@0 | 1933 | } |
michael@0 | 1934 | |
michael@0 | 1935 | nsPoint offset = aBuilder->ToReferenceFrame(this); |
michael@0 | 1936 | dirtyRect += offset; |
michael@0 | 1937 | |
michael@0 | 1938 | nsRect untransformedDirtyRect; |
michael@0 | 1939 | if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, offset, &untransformedDirtyRect)) { |
michael@0 | 1940 | dirtyRect = untransformedDirtyRect; |
michael@0 | 1941 | } else { |
michael@0 | 1942 | NS_WARNING("Unable to untransform dirty rect!"); |
michael@0 | 1943 | // This should only happen if the transform is singular, in which case nothing is visible anyway |
michael@0 | 1944 | dirtyRect.SetEmpty(); |
michael@0 | 1945 | } |
michael@0 | 1946 | } |
michael@0 | 1947 | inTransform = true; |
michael@0 | 1948 | } |
michael@0 | 1949 | |
michael@0 | 1950 | bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this); |
michael@0 | 1951 | bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL; |
michael@0 | 1952 | bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this); |
michael@0 | 1953 | bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY && |
michael@0 | 1954 | IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(), |
michael@0 | 1955 | nsLayoutUtils::SCROLLABLE_SAME_DOC | |
michael@0 | 1956 | nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN)); |
michael@0 | 1957 | |
michael@0 | 1958 | DisplayListClipState::AutoSaveRestore clipState(aBuilder); |
michael@0 | 1959 | |
michael@0 | 1960 | if (isTransformed || useOpacity || useBlendMode || usingSVGEffects || useStickyPosition) { |
michael@0 | 1961 | // We don't need to pass ancestor clipping down to our children; |
michael@0 | 1962 | // everything goes inside a display item's child list, and the display |
michael@0 | 1963 | // item itself will be clipped. |
michael@0 | 1964 | // For transforms we also need to clear ancestor clipping because it's |
michael@0 | 1965 | // relative to the wrong display item reference frame anyway. |
michael@0 | 1966 | clipState.Clear(); |
michael@0 | 1967 | } |
michael@0 | 1968 | |
michael@0 | 1969 | nsDisplayListCollection set; |
michael@0 | 1970 | { |
michael@0 | 1971 | nsDisplayListBuilder::AutoBuildingDisplayList rootSetter(aBuilder, true); |
michael@0 | 1972 | DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder); |
michael@0 | 1973 | nsDisplayListBuilder::AutoInTransformSetter |
michael@0 | 1974 | inTransformSetter(aBuilder, inTransform); |
michael@0 | 1975 | CheckForTouchEventHandler(aBuilder, this); |
michael@0 | 1976 | |
michael@0 | 1977 | if (usingSVGEffects) { |
michael@0 | 1978 | dirtyRect = |
michael@0 | 1979 | nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); |
michael@0 | 1980 | } |
michael@0 | 1981 | |
michael@0 | 1982 | nsRect clipPropClip; |
michael@0 | 1983 | if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip, |
michael@0 | 1984 | nestedClipState)) { |
michael@0 | 1985 | dirtyRect.IntersectRect(dirtyRect, clipPropClip); |
michael@0 | 1986 | } |
michael@0 | 1987 | |
michael@0 | 1988 | MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect); |
michael@0 | 1989 | |
michael@0 | 1990 | // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false |
michael@0 | 1991 | // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy |
michael@0 | 1992 | if (Preserves3DChildren()) { |
michael@0 | 1993 | aBuilder->MarkPreserve3DFramesForDisplayList(this, aDirtyRect); |
michael@0 | 1994 | } |
michael@0 | 1995 | |
michael@0 | 1996 | if (aBuilder->IsBuildingLayerEventRegions()) { |
michael@0 | 1997 | nsDisplayLayerEventRegions* eventRegions = |
michael@0 | 1998 | new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this); |
michael@0 | 1999 | aBuilder->SetLayerEventRegions(eventRegions); |
michael@0 | 2000 | set.BorderBackground()->AppendNewToTop(eventRegions); |
michael@0 | 2001 | } |
michael@0 | 2002 | BuildDisplayList(aBuilder, dirtyRect, set); |
michael@0 | 2003 | } |
michael@0 | 2004 | |
michael@0 | 2005 | if (aBuilder->IsBackgroundOnly()) { |
michael@0 | 2006 | set.BlockBorderBackgrounds()->DeleteAll(); |
michael@0 | 2007 | set.Floats()->DeleteAll(); |
michael@0 | 2008 | set.Content()->DeleteAll(); |
michael@0 | 2009 | set.PositionedDescendants()->DeleteAll(); |
michael@0 | 2010 | set.Outlines()->DeleteAll(); |
michael@0 | 2011 | } |
michael@0 | 2012 | |
michael@0 | 2013 | // This z-order sort also sorts secondarily by content order. We need to do |
michael@0 | 2014 | // this so that boxes produced by the same element are placed together |
michael@0 | 2015 | // in the sort. Consider a position:relative inline element that breaks |
michael@0 | 2016 | // across lines and has absolutely positioned children; all the abs-pos |
michael@0 | 2017 | // children should be z-ordered after all the boxes for the position:relative |
michael@0 | 2018 | // element itself. |
michael@0 | 2019 | set.PositionedDescendants()->SortByZOrder(aBuilder, GetContent()); |
michael@0 | 2020 | |
michael@0 | 2021 | nsDisplayList resultList; |
michael@0 | 2022 | // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html |
michael@0 | 2023 | // 1,2: backgrounds and borders |
michael@0 | 2024 | resultList.AppendToTop(set.BorderBackground()); |
michael@0 | 2025 | // 3: negative z-index children. |
michael@0 | 2026 | for (;;) { |
michael@0 | 2027 | nsDisplayItem* item = set.PositionedDescendants()->GetBottom(); |
michael@0 | 2028 | if (item && item->ZIndex() < 0) { |
michael@0 | 2029 | set.PositionedDescendants()->RemoveBottom(); |
michael@0 | 2030 | resultList.AppendToTop(item); |
michael@0 | 2031 | continue; |
michael@0 | 2032 | } |
michael@0 | 2033 | break; |
michael@0 | 2034 | } |
michael@0 | 2035 | // 4: block backgrounds |
michael@0 | 2036 | resultList.AppendToTop(set.BlockBorderBackgrounds()); |
michael@0 | 2037 | // 5: floats |
michael@0 | 2038 | resultList.AppendToTop(set.Floats()); |
michael@0 | 2039 | // 7: general content |
michael@0 | 2040 | resultList.AppendToTop(set.Content()); |
michael@0 | 2041 | // 7.5: outlines, in content tree order. We need to sort by content order |
michael@0 | 2042 | // because an element with outline that breaks and has children with outline |
michael@0 | 2043 | // might have placed child outline items between its own outline items. |
michael@0 | 2044 | // The element's outline items need to all come before any child outline |
michael@0 | 2045 | // items. |
michael@0 | 2046 | nsIContent* content = GetContent(); |
michael@0 | 2047 | if (!content) { |
michael@0 | 2048 | content = PresContext()->Document()->GetRootElement(); |
michael@0 | 2049 | } |
michael@0 | 2050 | if (content) { |
michael@0 | 2051 | set.Outlines()->SortByContentOrder(aBuilder, content); |
michael@0 | 2052 | } |
michael@0 | 2053 | #ifdef DEBUG |
michael@0 | 2054 | DisplayDebugBorders(aBuilder, this, set); |
michael@0 | 2055 | #endif |
michael@0 | 2056 | resultList.AppendToTop(set.Outlines()); |
michael@0 | 2057 | // 8, 9: non-negative z-index children |
michael@0 | 2058 | resultList.AppendToTop(set.PositionedDescendants()); |
michael@0 | 2059 | |
michael@0 | 2060 | if (!isTransformed) { |
michael@0 | 2061 | // Restore saved clip state now so that any display items we create below |
michael@0 | 2062 | // are clipped properly. |
michael@0 | 2063 | clipState.Restore(); |
michael@0 | 2064 | } |
michael@0 | 2065 | |
michael@0 | 2066 | /* If there are any SVG effects, wrap the list up in an SVG effects item |
michael@0 | 2067 | * (which also handles CSS group opacity). Note that we create an SVG effects |
michael@0 | 2068 | * item even if resultList is empty, since a filter can produce graphical |
michael@0 | 2069 | * output even if the element being filtered wouldn't otherwise do so. |
michael@0 | 2070 | */ |
michael@0 | 2071 | if (usingSVGEffects) { |
michael@0 | 2072 | /* List now emptied, so add the new list to the top. */ |
michael@0 | 2073 | resultList.AppendNewToTop( |
michael@0 | 2074 | new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList)); |
michael@0 | 2075 | } |
michael@0 | 2076 | /* Else, if the list is non-empty and there is CSS group opacity without SVG |
michael@0 | 2077 | * effects, wrap it up in an opacity item. |
michael@0 | 2078 | */ |
michael@0 | 2079 | else if (useOpacity && !resultList.IsEmpty()) { |
michael@0 | 2080 | resultList.AppendNewToTop( |
michael@0 | 2081 | new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList)); |
michael@0 | 2082 | } |
michael@0 | 2083 | /* If we have sticky positioning, wrap it in a sticky position item. |
michael@0 | 2084 | */ |
michael@0 | 2085 | if (useStickyPosition) { |
michael@0 | 2086 | resultList.AppendNewToTop( |
michael@0 | 2087 | new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList)); |
michael@0 | 2088 | } |
michael@0 | 2089 | |
michael@0 | 2090 | /* If we're going to apply a transformation and don't have preserve-3d set, wrap |
michael@0 | 2091 | * everything in an nsDisplayTransform. If there's nothing in the list, don't add |
michael@0 | 2092 | * anything. |
michael@0 | 2093 | * |
michael@0 | 2094 | * For the preserve-3d case we want to individually wrap every child in the list with |
michael@0 | 2095 | * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform, |
michael@0 | 2096 | * we can skip this step, as the computed transform will already include our own. |
michael@0 | 2097 | * |
michael@0 | 2098 | * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that |
michael@0 | 2099 | * we find all the correct children. |
michael@0 | 2100 | */ |
michael@0 | 2101 | if (isTransformed && !resultList.IsEmpty()) { |
michael@0 | 2102 | // Restore clip state now so nsDisplayTransform is clipped properly. |
michael@0 | 2103 | clipState.Restore(); |
michael@0 | 2104 | |
michael@0 | 2105 | if (Preserves3DChildren()) { |
michael@0 | 2106 | WrapPreserve3DList(this, aBuilder, &resultList); |
michael@0 | 2107 | } else { |
michael@0 | 2108 | resultList.AppendNewToTop( |
michael@0 | 2109 | new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList)); |
michael@0 | 2110 | } |
michael@0 | 2111 | } |
michael@0 | 2112 | |
michael@0 | 2113 | /* If adding both a nsDisplayBlendContainer and a nsDisplayMixBlendMode to the |
michael@0 | 2114 | * same list, the nsDisplayBlendContainer should be added first. This only |
michael@0 | 2115 | * happens when the element creating this stacking context has mix-blend-mode |
michael@0 | 2116 | * and also contains a child which has mix-blend-mode. |
michael@0 | 2117 | * The nsDisplayBlendContainer must be added to the list first, so it does not |
michael@0 | 2118 | * isolate the containing element blending as well. |
michael@0 | 2119 | */ |
michael@0 | 2120 | |
michael@0 | 2121 | if (aBuilder->ContainsBlendMode()) { |
michael@0 | 2122 | resultList.AppendNewToTop( |
michael@0 | 2123 | new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList)); |
michael@0 | 2124 | } |
michael@0 | 2125 | |
michael@0 | 2126 | /* If there's blending, wrap up the list in a blend-mode item. Note |
michael@0 | 2127 | * that opacity can be applied before blending as the blend color is |
michael@0 | 2128 | * not affected by foreground opacity (only background alpha). |
michael@0 | 2129 | */ |
michael@0 | 2130 | |
michael@0 | 2131 | if (useBlendMode && !resultList.IsEmpty()) { |
michael@0 | 2132 | resultList.AppendNewToTop( |
michael@0 | 2133 | new (aBuilder) nsDisplayMixBlendMode(aBuilder, this, &resultList)); |
michael@0 | 2134 | } |
michael@0 | 2135 | |
michael@0 | 2136 | CreateOwnLayerIfNeeded(aBuilder, &resultList); |
michael@0 | 2137 | |
michael@0 | 2138 | aList->AppendToTop(&resultList); |
michael@0 | 2139 | } |
michael@0 | 2140 | |
michael@0 | 2141 | static nsDisplayItem* |
michael@0 | 2142 | WrapInWrapList(nsDisplayListBuilder* aBuilder, |
michael@0 | 2143 | nsIFrame* aFrame, nsDisplayList* aList) |
michael@0 | 2144 | { |
michael@0 | 2145 | nsDisplayItem* item = aList->GetBottom(); |
michael@0 | 2146 | if (!item || item->GetAbove() || item->Frame() != aFrame) { |
michael@0 | 2147 | return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList); |
michael@0 | 2148 | } |
michael@0 | 2149 | aList->RemoveBottom(); |
michael@0 | 2150 | return item; |
michael@0 | 2151 | } |
michael@0 | 2152 | |
michael@0 | 2153 | void |
michael@0 | 2154 | nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, |
michael@0 | 2155 | nsIFrame* aChild, |
michael@0 | 2156 | const nsRect& aDirtyRect, |
michael@0 | 2157 | const nsDisplayListSet& aLists, |
michael@0 | 2158 | uint32_t aFlags) { |
michael@0 | 2159 | // If painting is restricted to just the background of the top level frame, |
michael@0 | 2160 | // then we have nothing to do here. |
michael@0 | 2161 | if (aBuilder->IsBackgroundOnly()) |
michael@0 | 2162 | return; |
michael@0 | 2163 | |
michael@0 | 2164 | nsIFrame* child = aChild; |
michael@0 | 2165 | if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) |
michael@0 | 2166 | return; |
michael@0 | 2167 | |
michael@0 | 2168 | bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); |
michael@0 | 2169 | |
michael@0 | 2170 | // true if this is a real or pseudo stacking context |
michael@0 | 2171 | bool pseudoStackingContext = |
michael@0 | 2172 | (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; |
michael@0 | 2173 | if (!isSVG && |
michael@0 | 2174 | (aFlags & DISPLAY_CHILD_INLINE) && |
michael@0 | 2175 | !child->IsFrameOfType(eLineParticipant)) { |
michael@0 | 2176 | // child is a non-inline frame in an inline context, i.e., |
michael@0 | 2177 | // it acts like inline-block or inline-table. Therefore it is a |
michael@0 | 2178 | // pseudo-stacking-context. |
michael@0 | 2179 | pseudoStackingContext = true; |
michael@0 | 2180 | } |
michael@0 | 2181 | |
michael@0 | 2182 | // dirty rect in child-relative coordinates |
michael@0 | 2183 | nsRect dirty = aDirtyRect - child->GetOffsetTo(this); |
michael@0 | 2184 | |
michael@0 | 2185 | nsIAtom* childType = child->GetType(); |
michael@0 | 2186 | nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr; |
michael@0 | 2187 | if (childType == nsGkAtoms::placeholderFrame) { |
michael@0 | 2188 | nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child); |
michael@0 | 2189 | child = placeholder->GetOutOfFlowFrame(); |
michael@0 | 2190 | NS_ASSERTION(child, "No out of flow frame?"); |
michael@0 | 2191 | // If 'child' is a pushed float then it's owned by a block that's not an |
michael@0 | 2192 | // ancestor of the placeholder, and it will be painted by that block and |
michael@0 | 2193 | // should not be painted through the placeholder. |
michael@0 | 2194 | if (!child || nsLayoutUtils::IsPopup(child) || |
michael@0 | 2195 | (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)) |
michael@0 | 2196 | return; |
michael@0 | 2197 | // Make sure that any attempt to use childType below is disappointed. We |
michael@0 | 2198 | // could call GetType again but since we don't currently need it, let's |
michael@0 | 2199 | // avoid the virtual call. |
michael@0 | 2200 | childType = nullptr; |
michael@0 | 2201 | // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
michael@0 | 2202 | if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) |
michael@0 | 2203 | return; |
michael@0 | 2204 | savedOutOfFlowData = static_cast<nsDisplayListBuilder::OutOfFlowDisplayData*> |
michael@0 | 2205 | (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty())); |
michael@0 | 2206 | if (savedOutOfFlowData) { |
michael@0 | 2207 | dirty = savedOutOfFlowData->mDirtyRect; |
michael@0 | 2208 | } else { |
michael@0 | 2209 | // The out-of-flow frame did not intersect the dirty area. We may still |
michael@0 | 2210 | // need to traverse into it, since it may contain placeholders we need |
michael@0 | 2211 | // to enter to reach other out-of-flow frames that are visible. |
michael@0 | 2212 | dirty.SetEmpty(); |
michael@0 | 2213 | } |
michael@0 | 2214 | pseudoStackingContext = true; |
michael@0 | 2215 | } |
michael@0 | 2216 | if (child->Preserves3D()) { |
michael@0 | 2217 | nsRect* savedDirty = static_cast<nsRect*> |
michael@0 | 2218 | (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty())); |
michael@0 | 2219 | if (savedDirty) { |
michael@0 | 2220 | dirty = *savedDirty; |
michael@0 | 2221 | } else { |
michael@0 | 2222 | dirty.SetEmpty(); |
michael@0 | 2223 | } |
michael@0 | 2224 | } |
michael@0 | 2225 | |
michael@0 | 2226 | NS_ASSERTION(childType != nsGkAtoms::placeholderFrame, |
michael@0 | 2227 | "Should have dealt with placeholders already"); |
michael@0 | 2228 | if (aBuilder->GetSelectedFramesOnly() && |
michael@0 | 2229 | child->IsLeaf() && |
michael@0 | 2230 | !aChild->IsSelected()) { |
michael@0 | 2231 | return; |
michael@0 | 2232 | } |
michael@0 | 2233 | |
michael@0 | 2234 | if (aBuilder->GetIncludeAllOutOfFlows() && |
michael@0 | 2235 | (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { |
michael@0 | 2236 | dirty = child->GetVisualOverflowRect(); |
michael@0 | 2237 | } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { |
michael@0 | 2238 | // No need to descend into child to catch placeholders for visible |
michael@0 | 2239 | // positioned stuff. So see if we can short-circuit frame traversal here. |
michael@0 | 2240 | |
michael@0 | 2241 | // We can stop if child's frame subtree's intersection with the |
michael@0 | 2242 | // dirty area is empty. |
michael@0 | 2243 | // If the child is a scrollframe that we want to ignore, then we need |
michael@0 | 2244 | // to descend into it because its scrolled child may intersect the dirty |
michael@0 | 2245 | // area even if the scrollframe itself doesn't. |
michael@0 | 2246 | // There are cases where the "ignore scroll frame" on the builder is not set |
michael@0 | 2247 | // correctly, and so we additionally want to catch cases where the child is |
michael@0 | 2248 | // a root scrollframe and we are ignoring scrolling on the viewport. |
michael@0 | 2249 | nsIPresShell* shell = PresContext()->PresShell(); |
michael@0 | 2250 | bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() || |
michael@0 | 2251 | (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame()); |
michael@0 | 2252 | if (!keepDescending) { |
michael@0 | 2253 | nsRect childDirty; |
michael@0 | 2254 | if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) |
michael@0 | 2255 | return; |
michael@0 | 2256 | // Usually we could set dirty to childDirty now but there's no |
michael@0 | 2257 | // benefit, and it can be confusing. It can especially confuse |
michael@0 | 2258 | // situations where we're going to ignore a scrollframe's clipping; |
michael@0 | 2259 | // we wouldn't want to clip the dirty area to the scrollframe's |
michael@0 | 2260 | // bounds in that case. |
michael@0 | 2261 | } |
michael@0 | 2262 | } |
michael@0 | 2263 | |
michael@0 | 2264 | // XXX need to have inline-block and inline-table set pseudoStackingContext |
michael@0 | 2265 | |
michael@0 | 2266 | const nsStyleDisplay* ourDisp = StyleDisplay(); |
michael@0 | 2267 | // REVIEW: Taken from nsBoxFrame::Paint |
michael@0 | 2268 | // Don't paint our children if the theme object is a leaf. |
michael@0 | 2269 | if (IsThemed(ourDisp) && |
michael@0 | 2270 | !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance)) |
michael@0 | 2271 | return; |
michael@0 | 2272 | |
michael@0 | 2273 | // Child is composited if it's transformed, partially transparent, or has |
michael@0 | 2274 | // SVG effects or a blend mode.. |
michael@0 | 2275 | const nsStyleDisplay* disp = child->StyleDisplay(); |
michael@0 | 2276 | const nsStylePosition* pos = child->StylePosition(); |
michael@0 | 2277 | bool isVisuallyAtomic = child->HasOpacity() |
michael@0 | 2278 | || child->IsTransformed() |
michael@0 | 2279 | // strictly speaking, 'perspective' doesn't require visual atomicity, |
michael@0 | 2280 | // but the spec says it acts like the rest of these |
michael@0 | 2281 | || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord |
michael@0 | 2282 | || disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL |
michael@0 | 2283 | || nsSVGIntegrationUtils::UsingEffectsForFrame(child); |
michael@0 | 2284 | |
michael@0 | 2285 | bool isPositioned = disp->IsPositioned(child); |
michael@0 | 2286 | bool isStackingContext = |
michael@0 | 2287 | (isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY || |
michael@0 | 2288 | pos->mZIndex.GetUnit() == eStyleUnit_Integer)) || |
michael@0 | 2289 | (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || |
michael@0 | 2290 | isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT); |
michael@0 | 2291 | |
michael@0 | 2292 | if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) || |
michael@0 | 2293 | ((disp->mClipFlags & NS_STYLE_CLIP_RECT) && |
michael@0 | 2294 | IsSVGContentWithCSSClip(child)) || |
michael@0 | 2295 | (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || |
michael@0 | 2296 | (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { |
michael@0 | 2297 | // If you change this, also change IsPseudoStackingContextFromStyle() |
michael@0 | 2298 | pseudoStackingContext = true; |
michael@0 | 2299 | } |
michael@0 | 2300 | NS_ASSERTION(!isStackingContext || pseudoStackingContext, |
michael@0 | 2301 | "Stacking contexts must also be pseudo-stacking-contexts"); |
michael@0 | 2302 | |
michael@0 | 2303 | bool isInFixedPos = aBuilder->IsInFixedPos() || |
michael@0 | 2304 | (isPositioned && |
michael@0 | 2305 | disp->mPosition == NS_STYLE_POSITION_FIXED && |
michael@0 | 2306 | nsLayoutUtils::IsReallyFixedPos(child)); |
michael@0 | 2307 | nsDisplayListBuilder::AutoInFixedPosSetter |
michael@0 | 2308 | buildingInFixedPos(aBuilder, isInFixedPos); |
michael@0 | 2309 | |
michael@0 | 2310 | nsDisplayListBuilder::AutoBuildingDisplayList |
michael@0 | 2311 | buildingForChild(aBuilder, child, pseudoStackingContext); |
michael@0 | 2312 | DisplayListClipState::AutoClipMultiple clipState(aBuilder); |
michael@0 | 2313 | CheckForTouchEventHandler(aBuilder, child); |
michael@0 | 2314 | |
michael@0 | 2315 | if (savedOutOfFlowData) { |
michael@0 | 2316 | clipState.SetClipForContainingBlockDescendants( |
michael@0 | 2317 | &savedOutOfFlowData->mContainingBlockClip); |
michael@0 | 2318 | } |
michael@0 | 2319 | |
michael@0 | 2320 | // Setup clipping for the parent's overflow:-moz-hidden-unscrollable, |
michael@0 | 2321 | // or overflow:hidden on elements that don't support scrolling (and therefore |
michael@0 | 2322 | // don't create nsHTML/XULScrollFrame). This clipping needs to not clip |
michael@0 | 2323 | // anything directly rendered by the parent, only the rendering of its |
michael@0 | 2324 | // children. |
michael@0 | 2325 | // Don't use overflowClip to restrict the dirty rect, since some of the |
michael@0 | 2326 | // descendants may not be clipped by it. Even if we end up with unnecessary |
michael@0 | 2327 | // display items, they'll be pruned during ComputeVisibility. |
michael@0 | 2328 | nsIFrame* parent = child->GetParent(); |
michael@0 | 2329 | const nsStyleDisplay* parentDisp = |
michael@0 | 2330 | parent == this ? ourDisp : parent->StyleDisplay(); |
michael@0 | 2331 | ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState); |
michael@0 | 2332 | |
michael@0 | 2333 | nsDisplayList list; |
michael@0 | 2334 | nsDisplayList extraPositionedDescendants; |
michael@0 | 2335 | if (isStackingContext) { |
michael@0 | 2336 | if (disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
michael@0 | 2337 | aBuilder->SetContainsBlendMode(true); |
michael@0 | 2338 | } |
michael@0 | 2339 | // True stacking context. |
michael@0 | 2340 | // For stacking contexts, BuildDisplayListForStackingContext handles |
michael@0 | 2341 | // clipping and MarkAbsoluteFramesForDisplayList. |
michael@0 | 2342 | child->BuildDisplayListForStackingContext(aBuilder, dirty, &list); |
michael@0 | 2343 | aBuilder->DisplayCaret(child, dirty, &list); |
michael@0 | 2344 | } else { |
michael@0 | 2345 | nsRect clipRect; |
michael@0 | 2346 | if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect, clipState)) { |
michael@0 | 2347 | // clipRect is in builder-reference-frame coordinates, |
michael@0 | 2348 | // dirty/clippedDirtyRect are in child coordinates |
michael@0 | 2349 | dirty.IntersectRect(dirty, clipRect); |
michael@0 | 2350 | } |
michael@0 | 2351 | |
michael@0 | 2352 | child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); |
michael@0 | 2353 | |
michael@0 | 2354 | if (!pseudoStackingContext) { |
michael@0 | 2355 | // THIS IS THE COMMON CASE. |
michael@0 | 2356 | // Not a pseudo or real stacking context. Do the simple thing and |
michael@0 | 2357 | // return early. |
michael@0 | 2358 | nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); |
michael@0 | 2359 | if (eventRegions) { |
michael@0 | 2360 | eventRegions->AddFrame(aBuilder, child); |
michael@0 | 2361 | } |
michael@0 | 2362 | child->BuildDisplayList(aBuilder, dirty, aLists); |
michael@0 | 2363 | aBuilder->DisplayCaret(child, dirty, aLists.Content()); |
michael@0 | 2364 | #ifdef DEBUG |
michael@0 | 2365 | DisplayDebugBorders(aBuilder, child, aLists); |
michael@0 | 2366 | #endif |
michael@0 | 2367 | return; |
michael@0 | 2368 | } |
michael@0 | 2369 | |
michael@0 | 2370 | // A pseudo-stacking context (e.g., a positioned element with z-index auto). |
michael@0 | 2371 | // We allow positioned descendants of the child to escape to our parent |
michael@0 | 2372 | // stacking context's positioned descendant list, because they might be |
michael@0 | 2373 | // z-index:non-auto |
michael@0 | 2374 | nsDisplayListCollection pseudoStack; |
michael@0 | 2375 | if (aBuilder->IsBuildingLayerEventRegions()) { |
michael@0 | 2376 | nsDisplayLayerEventRegions* eventRegions = |
michael@0 | 2377 | new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this); |
michael@0 | 2378 | aBuilder->SetLayerEventRegions(eventRegions); |
michael@0 | 2379 | pseudoStack.BorderBackground()->AppendNewToTop(eventRegions); |
michael@0 | 2380 | } |
michael@0 | 2381 | child->BuildDisplayList(aBuilder, dirty, pseudoStack); |
michael@0 | 2382 | aBuilder->DisplayCaret(child, dirty, pseudoStack.Content()); |
michael@0 | 2383 | |
michael@0 | 2384 | list.AppendToTop(pseudoStack.BorderBackground()); |
michael@0 | 2385 | list.AppendToTop(pseudoStack.BlockBorderBackgrounds()); |
michael@0 | 2386 | list.AppendToTop(pseudoStack.Floats()); |
michael@0 | 2387 | list.AppendToTop(pseudoStack.Content()); |
michael@0 | 2388 | list.AppendToTop(pseudoStack.Outlines()); |
michael@0 | 2389 | extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants()); |
michael@0 | 2390 | #ifdef DEBUG |
michael@0 | 2391 | DisplayDebugBorders(aBuilder, child, aLists); |
michael@0 | 2392 | #endif |
michael@0 | 2393 | } |
michael@0 | 2394 | |
michael@0 | 2395 | // Clear clip rect for the construction of the items below. Since we're |
michael@0 | 2396 | // clipping all their contents, they themselves don't need to be clipped. |
michael@0 | 2397 | clipState.Clear(); |
michael@0 | 2398 | |
michael@0 | 2399 | if (isPositioned || isVisuallyAtomic || |
michael@0 | 2400 | (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { |
michael@0 | 2401 | // Genuine stacking contexts, and positioned pseudo-stacking-contexts, |
michael@0 | 2402 | // go in this level. |
michael@0 | 2403 | if (!list.IsEmpty()) { |
michael@0 | 2404 | nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list); |
michael@0 | 2405 | if (isSVG) { |
michael@0 | 2406 | aLists.Content()->AppendNewToTop(item); |
michael@0 | 2407 | } else { |
michael@0 | 2408 | aLists.PositionedDescendants()->AppendNewToTop(item); |
michael@0 | 2409 | } |
michael@0 | 2410 | } |
michael@0 | 2411 | } else if (!isSVG && disp->IsFloating(child)) { |
michael@0 | 2412 | if (!list.IsEmpty()) { |
michael@0 | 2413 | aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list)); |
michael@0 | 2414 | } |
michael@0 | 2415 | } else { |
michael@0 | 2416 | aLists.Content()->AppendToTop(&list); |
michael@0 | 2417 | } |
michael@0 | 2418 | // We delay placing the positioned descendants of positioned frames to here, |
michael@0 | 2419 | // because in the absence of z-index this is the correct order for them. |
michael@0 | 2420 | // This doesn't affect correctness because the positioned descendants list |
michael@0 | 2421 | // is sorted by z-order and content in BuildDisplayListForStackingContext, |
michael@0 | 2422 | // but it means that sort routine needs to do less work. |
michael@0 | 2423 | aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants); |
michael@0 | 2424 | } |
michael@0 | 2425 | |
michael@0 | 2426 | void |
michael@0 | 2427 | nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 2428 | const nsRect& aDirtyRect) |
michael@0 | 2429 | { |
michael@0 | 2430 | if (IsAbsoluteContainer()) { |
michael@0 | 2431 | aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect); |
michael@0 | 2432 | } |
michael@0 | 2433 | } |
michael@0 | 2434 | |
michael@0 | 2435 | nsresult |
michael@0 | 2436 | nsFrame::GetContentForEvent(WidgetEvent* aEvent, |
michael@0 | 2437 | nsIContent** aContent) |
michael@0 | 2438 | { |
michael@0 | 2439 | nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this); |
michael@0 | 2440 | *aContent = f->GetContent(); |
michael@0 | 2441 | NS_IF_ADDREF(*aContent); |
michael@0 | 2442 | return NS_OK; |
michael@0 | 2443 | } |
michael@0 | 2444 | |
michael@0 | 2445 | void |
michael@0 | 2446 | nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent) |
michael@0 | 2447 | { |
michael@0 | 2448 | nsIContent* target = aContent ? aContent : mContent; |
michael@0 | 2449 | |
michael@0 | 2450 | if (target) { |
michael@0 | 2451 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 2452 | new AsyncEventDispatcher(target, aDOMEventName, true, false); |
michael@0 | 2453 | DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent(); |
michael@0 | 2454 | NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch"); |
michael@0 | 2455 | } |
michael@0 | 2456 | } |
michael@0 | 2457 | |
michael@0 | 2458 | nsresult |
michael@0 | 2459 | nsFrame::HandleEvent(nsPresContext* aPresContext, |
michael@0 | 2460 | WidgetGUIEvent* aEvent, |
michael@0 | 2461 | nsEventStatus* aEventStatus) |
michael@0 | 2462 | { |
michael@0 | 2463 | |
michael@0 | 2464 | if (aEvent->message == NS_MOUSE_MOVE) { |
michael@0 | 2465 | // XXX If the second argument of HandleDrag() is WidgetMouseEvent, |
michael@0 | 2466 | // the implementation becomes simpler. |
michael@0 | 2467 | return HandleDrag(aPresContext, aEvent, aEventStatus); |
michael@0 | 2468 | } |
michael@0 | 2469 | |
michael@0 | 2470 | if ((aEvent->eventStructType == NS_MOUSE_EVENT && |
michael@0 | 2471 | aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) || |
michael@0 | 2472 | aEvent->eventStructType == NS_TOUCH_EVENT) { |
michael@0 | 2473 | if (aEvent->message == NS_MOUSE_BUTTON_DOWN || aEvent->message == NS_TOUCH_START) { |
michael@0 | 2474 | HandlePress(aPresContext, aEvent, aEventStatus); |
michael@0 | 2475 | } else if (aEvent->message == NS_MOUSE_BUTTON_UP || aEvent->message == NS_TOUCH_END) { |
michael@0 | 2476 | HandleRelease(aPresContext, aEvent, aEventStatus); |
michael@0 | 2477 | } |
michael@0 | 2478 | } |
michael@0 | 2479 | return NS_OK; |
michael@0 | 2480 | } |
michael@0 | 2481 | |
michael@0 | 2482 | NS_IMETHODIMP |
michael@0 | 2483 | nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection, |
michael@0 | 2484 | nsIPresShell* aPresShell, |
michael@0 | 2485 | WidgetMouseEvent* aMouseEvent, |
michael@0 | 2486 | nsIContent** aParentContent, |
michael@0 | 2487 | int32_t* aContentOffset, |
michael@0 | 2488 | int32_t* aTarget) |
michael@0 | 2489 | { |
michael@0 | 2490 | if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget) |
michael@0 | 2491 | return NS_ERROR_NULL_POINTER; |
michael@0 | 2492 | |
michael@0 | 2493 | *aParentContent = nullptr; |
michael@0 | 2494 | *aContentOffset = 0; |
michael@0 | 2495 | *aTarget = 0; |
michael@0 | 2496 | |
michael@0 | 2497 | int16_t displaySelection = aPresShell->GetSelectionFlags(); |
michael@0 | 2498 | |
michael@0 | 2499 | bool selectingTableCells = aFrameSelection->GetTableCellSelection(); |
michael@0 | 2500 | |
michael@0 | 2501 | // DISPLAY_ALL means we're in an editor. |
michael@0 | 2502 | // If already in cell selection mode, |
michael@0 | 2503 | // continue selecting with mouse drag or end on mouse up, |
michael@0 | 2504 | // or when using shift key to extend block of cells |
michael@0 | 2505 | // (Mouse down does normal selection unless Ctrl/Cmd is pressed) |
michael@0 | 2506 | bool doTableSelection = |
michael@0 | 2507 | displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells && |
michael@0 | 2508 | (aMouseEvent->message == NS_MOUSE_MOVE || |
michael@0 | 2509 | (aMouseEvent->message == NS_MOUSE_BUTTON_UP && |
michael@0 | 2510 | aMouseEvent->button == WidgetMouseEvent::eLeftButton) || |
michael@0 | 2511 | aMouseEvent->IsShift()); |
michael@0 | 2512 | |
michael@0 | 2513 | if (!doTableSelection) |
michael@0 | 2514 | { |
michael@0 | 2515 | // In Browser, special 'table selection' key must be pressed for table selection |
michael@0 | 2516 | // or when just Shift is pressed and we're already in table/cell selection mode |
michael@0 | 2517 | #ifdef XP_MACOSX |
michael@0 | 2518 | doTableSelection = aMouseEvent->IsMeta() || (aMouseEvent->IsShift() && selectingTableCells); |
michael@0 | 2519 | #else |
michael@0 | 2520 | doTableSelection = aMouseEvent->IsControl() || (aMouseEvent->IsShift() && selectingTableCells); |
michael@0 | 2521 | #endif |
michael@0 | 2522 | } |
michael@0 | 2523 | if (!doTableSelection) |
michael@0 | 2524 | return NS_OK; |
michael@0 | 2525 | |
michael@0 | 2526 | // Get the cell frame or table frame (or parent) of the current content node |
michael@0 | 2527 | nsIFrame *frame = this; |
michael@0 | 2528 | bool foundCell = false; |
michael@0 | 2529 | bool foundTable = false; |
michael@0 | 2530 | |
michael@0 | 2531 | // Get the limiting node to stop parent frame search |
michael@0 | 2532 | nsIContent* limiter = aFrameSelection->GetLimiter(); |
michael@0 | 2533 | |
michael@0 | 2534 | // If our content node is an ancestor of the limiting node, |
michael@0 | 2535 | // we should stop the search right now. |
michael@0 | 2536 | if (limiter && nsContentUtils::ContentIsDescendantOf(limiter, GetContent())) |
michael@0 | 2537 | return NS_OK; |
michael@0 | 2538 | |
michael@0 | 2539 | //We don't initiate row/col selection from here now, |
michael@0 | 2540 | // but we may in future |
michael@0 | 2541 | //bool selectColumn = false; |
michael@0 | 2542 | //bool selectRow = false; |
michael@0 | 2543 | |
michael@0 | 2544 | while (frame) |
michael@0 | 2545 | { |
michael@0 | 2546 | // Check for a table cell by querying to a known CellFrame interface |
michael@0 | 2547 | nsITableCellLayout *cellElement = do_QueryFrame(frame); |
michael@0 | 2548 | if (cellElement) |
michael@0 | 2549 | { |
michael@0 | 2550 | foundCell = true; |
michael@0 | 2551 | //TODO: If we want to use proximity to top or left border |
michael@0 | 2552 | // for row and column selection, this is the place to do it |
michael@0 | 2553 | break; |
michael@0 | 2554 | } |
michael@0 | 2555 | else |
michael@0 | 2556 | { |
michael@0 | 2557 | // If not a cell, check for table |
michael@0 | 2558 | // This will happen when starting frame is the table or child of a table, |
michael@0 | 2559 | // such as a row (we were inbetween cells or in table border) |
michael@0 | 2560 | nsTableOuterFrame *tableFrame = do_QueryFrame(frame); |
michael@0 | 2561 | if (tableFrame) |
michael@0 | 2562 | { |
michael@0 | 2563 | foundTable = true; |
michael@0 | 2564 | //TODO: How can we select row when along left table edge |
michael@0 | 2565 | // or select column when along top edge? |
michael@0 | 2566 | break; |
michael@0 | 2567 | } else { |
michael@0 | 2568 | frame = frame->GetParent(); |
michael@0 | 2569 | // Stop if we have hit the selection's limiting content node |
michael@0 | 2570 | if (frame && frame->GetContent() == limiter) |
michael@0 | 2571 | break; |
michael@0 | 2572 | } |
michael@0 | 2573 | } |
michael@0 | 2574 | } |
michael@0 | 2575 | // We aren't in a cell or table |
michael@0 | 2576 | if (!foundCell && !foundTable) return NS_OK; |
michael@0 | 2577 | |
michael@0 | 2578 | nsIContent* tableOrCellContent = frame->GetContent(); |
michael@0 | 2579 | if (!tableOrCellContent) return NS_ERROR_FAILURE; |
michael@0 | 2580 | |
michael@0 | 2581 | nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent(); |
michael@0 | 2582 | if (!parentContent) return NS_ERROR_FAILURE; |
michael@0 | 2583 | |
michael@0 | 2584 | int32_t offset = parentContent->IndexOf(tableOrCellContent); |
michael@0 | 2585 | // Not likely? |
michael@0 | 2586 | if (offset < 0) return NS_ERROR_FAILURE; |
michael@0 | 2587 | |
michael@0 | 2588 | // Everything is OK -- set the return values |
michael@0 | 2589 | *aParentContent = parentContent; |
michael@0 | 2590 | NS_ADDREF(*aParentContent); |
michael@0 | 2591 | |
michael@0 | 2592 | *aContentOffset = offset; |
michael@0 | 2593 | |
michael@0 | 2594 | #if 0 |
michael@0 | 2595 | if (selectRow) |
michael@0 | 2596 | *aTarget = nsISelectionPrivate::TABLESELECTION_ROW; |
michael@0 | 2597 | else if (selectColumn) |
michael@0 | 2598 | *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN; |
michael@0 | 2599 | else |
michael@0 | 2600 | #endif |
michael@0 | 2601 | if (foundCell) |
michael@0 | 2602 | *aTarget = nsISelectionPrivate::TABLESELECTION_CELL; |
michael@0 | 2603 | else if (foundTable) |
michael@0 | 2604 | *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE; |
michael@0 | 2605 | |
michael@0 | 2606 | return NS_OK; |
michael@0 | 2607 | } |
michael@0 | 2608 | |
michael@0 | 2609 | nsresult |
michael@0 | 2610 | nsFrame::IsSelectable(bool* aSelectable, uint8_t* aSelectStyle) const |
michael@0 | 2611 | { |
michael@0 | 2612 | if (!aSelectable) //it's ok if aSelectStyle is null |
michael@0 | 2613 | return NS_ERROR_NULL_POINTER; |
michael@0 | 2614 | |
michael@0 | 2615 | // Like 'visibility', we must check all the parents: if a parent |
michael@0 | 2616 | // is not selectable, none of its children is selectable. |
michael@0 | 2617 | // |
michael@0 | 2618 | // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all', |
michael@0 | 2619 | // all its children are selectable, even those with 'user-select:none'. |
michael@0 | 2620 | // |
michael@0 | 2621 | // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy, |
michael@0 | 2622 | // aSelectStyle returns the first style that is not AUTO. If these values |
michael@0 | 2623 | // are present in the frame hierarchy, aSelectStyle returns the style of the |
michael@0 | 2624 | // topmost parent that has either 'none' or '-moz-all'. |
michael@0 | 2625 | // |
michael@0 | 2626 | // For instance, if the frame hierarchy is: |
michael@0 | 2627 | // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is _MOZ_ALL |
michael@0 | 2628 | // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT |
michael@0 | 2629 | // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is _MOZ_ALL |
michael@0 | 2630 | // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT |
michael@0 | 2631 | // |
michael@0 | 2632 | uint8_t selectStyle = NS_STYLE_USER_SELECT_AUTO; |
michael@0 | 2633 | nsIFrame* frame = const_cast<nsFrame*>(this); |
michael@0 | 2634 | |
michael@0 | 2635 | while (frame) { |
michael@0 | 2636 | const nsStyleUIReset* userinterface = frame->StyleUIReset(); |
michael@0 | 2637 | switch (userinterface->mUserSelect) { |
michael@0 | 2638 | case NS_STYLE_USER_SELECT_ALL: |
michael@0 | 2639 | case NS_STYLE_USER_SELECT_MOZ_ALL: |
michael@0 | 2640 | // override the previous values |
michael@0 | 2641 | selectStyle = userinterface->mUserSelect; |
michael@0 | 2642 | break; |
michael@0 | 2643 | default: |
michael@0 | 2644 | // otherwise return the first value which is not 'auto' |
michael@0 | 2645 | if (selectStyle == NS_STYLE_USER_SELECT_AUTO) { |
michael@0 | 2646 | selectStyle = userinterface->mUserSelect; |
michael@0 | 2647 | } |
michael@0 | 2648 | break; |
michael@0 | 2649 | } |
michael@0 | 2650 | frame = frame->GetParent(); |
michael@0 | 2651 | } |
michael@0 | 2652 | |
michael@0 | 2653 | // convert internal values to standard values |
michael@0 | 2654 | if (selectStyle == NS_STYLE_USER_SELECT_AUTO) |
michael@0 | 2655 | selectStyle = NS_STYLE_USER_SELECT_TEXT; |
michael@0 | 2656 | else |
michael@0 | 2657 | if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL) |
michael@0 | 2658 | selectStyle = NS_STYLE_USER_SELECT_ALL; |
michael@0 | 2659 | |
michael@0 | 2660 | // return stuff |
michael@0 | 2661 | if (aSelectStyle) |
michael@0 | 2662 | *aSelectStyle = selectStyle; |
michael@0 | 2663 | if (mState & NS_FRAME_GENERATED_CONTENT) |
michael@0 | 2664 | *aSelectable = false; |
michael@0 | 2665 | else |
michael@0 | 2666 | *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE); |
michael@0 | 2667 | return NS_OK; |
michael@0 | 2668 | } |
michael@0 | 2669 | |
michael@0 | 2670 | /** |
michael@0 | 2671 | * Handles the Mouse Press Event for the frame |
michael@0 | 2672 | */ |
michael@0 | 2673 | NS_IMETHODIMP |
michael@0 | 2674 | nsFrame::HandlePress(nsPresContext* aPresContext, |
michael@0 | 2675 | WidgetGUIEvent* aEvent, |
michael@0 | 2676 | nsEventStatus* aEventStatus) |
michael@0 | 2677 | { |
michael@0 | 2678 | NS_ENSURE_ARG_POINTER(aEventStatus); |
michael@0 | 2679 | if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { |
michael@0 | 2680 | return NS_OK; |
michael@0 | 2681 | } |
michael@0 | 2682 | |
michael@0 | 2683 | NS_ENSURE_ARG_POINTER(aEvent); |
michael@0 | 2684 | if (aEvent->eventStructType == NS_TOUCH_EVENT) { |
michael@0 | 2685 | return NS_OK; |
michael@0 | 2686 | } |
michael@0 | 2687 | |
michael@0 | 2688 | //We often get out of sync state issues with mousedown events that |
michael@0 | 2689 | //get interrupted by alerts/dialogs. |
michael@0 | 2690 | //Check with the ESM to see if we should process this one |
michael@0 | 2691 | if (!aPresContext->EventStateManager()->EventStatusOK(aEvent)) |
michael@0 | 2692 | return NS_OK; |
michael@0 | 2693 | |
michael@0 | 2694 | nsresult rv; |
michael@0 | 2695 | nsIPresShell *shell = aPresContext->GetPresShell(); |
michael@0 | 2696 | if (!shell) |
michael@0 | 2697 | return NS_ERROR_FAILURE; |
michael@0 | 2698 | |
michael@0 | 2699 | // if we are in Navigator and the click is in a draggable node, we don't want |
michael@0 | 2700 | // to start selection because we don't want to interfere with a potential |
michael@0 | 2701 | // drag of said node and steal all its glory. |
michael@0 | 2702 | int16_t isEditor = shell->GetSelectionFlags(); |
michael@0 | 2703 | //weaaak. only the editor can display frame selection not just text and images |
michael@0 | 2704 | isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL; |
michael@0 | 2705 | |
michael@0 | 2706 | WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
michael@0 | 2707 | |
michael@0 | 2708 | if (!mouseEvent->IsAlt()) { |
michael@0 | 2709 | for (nsIContent* content = mContent; content; |
michael@0 | 2710 | content = content->GetParent()) { |
michael@0 | 2711 | if (nsContentUtils::ContentIsDraggable(content) && |
michael@0 | 2712 | !content->IsEditable()) { |
michael@0 | 2713 | // coordinate stuff is the fix for bug #55921 |
michael@0 | 2714 | if ((mRect - GetPosition()).Contains( |
michael@0 | 2715 | nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this))) { |
michael@0 | 2716 | return NS_OK; |
michael@0 | 2717 | } |
michael@0 | 2718 | } |
michael@0 | 2719 | } |
michael@0 | 2720 | } |
michael@0 | 2721 | |
michael@0 | 2722 | // check whether style allows selection |
michael@0 | 2723 | // if not, don't tell selection the mouse event even occurred. |
michael@0 | 2724 | bool selectable; |
michael@0 | 2725 | uint8_t selectStyle; |
michael@0 | 2726 | rv = IsSelectable(&selectable, &selectStyle); |
michael@0 | 2727 | if (NS_FAILED(rv)) return rv; |
michael@0 | 2728 | |
michael@0 | 2729 | // check for select: none |
michael@0 | 2730 | if (!selectable) |
michael@0 | 2731 | return NS_OK; |
michael@0 | 2732 | |
michael@0 | 2733 | // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and |
michael@0 | 2734 | // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic |
michael@0 | 2735 | bool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT); |
michael@0 | 2736 | |
michael@0 | 2737 | // If the mouse is dragged outside the nearest enclosing scrollable area |
michael@0 | 2738 | // while making a selection, the area will be scrolled. To do this, capture |
michael@0 | 2739 | // the mouse on the nearest scrollable frame. If there isn't a scrollable |
michael@0 | 2740 | // frame, or something else is already capturing the mouse, there's no |
michael@0 | 2741 | // reason to capture. |
michael@0 | 2742 | bool hasCapturedContent = false; |
michael@0 | 2743 | if (!nsIPresShell::GetCapturingContent()) { |
michael@0 | 2744 | nsIScrollableFrame* scrollFrame = |
michael@0 | 2745 | nsLayoutUtils::GetNearestScrollableFrame(this, |
michael@0 | 2746 | nsLayoutUtils::SCROLLABLE_SAME_DOC | |
michael@0 | 2747 | nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); |
michael@0 | 2748 | if (scrollFrame) { |
michael@0 | 2749 | nsIFrame* capturingFrame = do_QueryFrame(scrollFrame); |
michael@0 | 2750 | nsIPresShell::SetCapturingContent(capturingFrame->GetContent(), |
michael@0 | 2751 | CAPTURE_IGNOREALLOWED); |
michael@0 | 2752 | hasCapturedContent = true; |
michael@0 | 2753 | } |
michael@0 | 2754 | } |
michael@0 | 2755 | |
michael@0 | 2756 | // XXX This is screwy; it really should use the selection frame, not the |
michael@0 | 2757 | // event frame |
michael@0 | 2758 | const nsFrameSelection* frameselection = nullptr; |
michael@0 | 2759 | if (useFrameSelection) |
michael@0 | 2760 | frameselection = GetConstFrameSelection(); |
michael@0 | 2761 | else |
michael@0 | 2762 | frameselection = shell->ConstFrameSelection(); |
michael@0 | 2763 | |
michael@0 | 2764 | if (!frameselection || frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF) |
michael@0 | 2765 | return NS_OK;//nothing to do we cannot affect selection from here |
michael@0 | 2766 | |
michael@0 | 2767 | #ifdef XP_MACOSX |
michael@0 | 2768 | if (mouseEvent->IsControl()) |
michael@0 | 2769 | return NS_OK;//short circuit. hard coded for mac due to time restraints. |
michael@0 | 2770 | bool control = mouseEvent->IsMeta(); |
michael@0 | 2771 | #else |
michael@0 | 2772 | bool control = mouseEvent->IsControl(); |
michael@0 | 2773 | #endif |
michael@0 | 2774 | |
michael@0 | 2775 | nsRefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection); |
michael@0 | 2776 | if (mouseEvent->clickCount > 1) { |
michael@0 | 2777 | // These methods aren't const but can't actually delete anything, |
michael@0 | 2778 | // so no need for nsWeakFrame. |
michael@0 | 2779 | fc->SetMouseDownState(true); |
michael@0 | 2780 | fc->SetMouseDoubleDown(true); |
michael@0 | 2781 | return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control); |
michael@0 | 2782 | } |
michael@0 | 2783 | |
michael@0 | 2784 | nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); |
michael@0 | 2785 | ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN); |
michael@0 | 2786 | |
michael@0 | 2787 | if (!offsets.content) |
michael@0 | 2788 | return NS_ERROR_FAILURE; |
michael@0 | 2789 | |
michael@0 | 2790 | // On touchables devices, touch the screen is usually a pan action, |
michael@0 | 2791 | // so let's reposition the caret if needed but do not select text |
michael@0 | 2792 | // if the touch did not happen over an editable element. Otherwise, |
michael@0 | 2793 | // let the user move the caret by tapping and dragging. |
michael@0 | 2794 | if (!offsets.content->IsEditable() && |
michael@0 | 2795 | Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) { |
michael@0 | 2796 | // On touchables devices, mouse events are generated if the gesture is a tap. |
michael@0 | 2797 | // Such events are never going to generate a drag action, so let's release |
michael@0 | 2798 | // captured content if any. |
michael@0 | 2799 | if (hasCapturedContent) { |
michael@0 | 2800 | nsIPresShell::SetCapturingContent(nullptr, 0); |
michael@0 | 2801 | } |
michael@0 | 2802 | |
michael@0 | 2803 | return fc->HandleClick(offsets.content, offsets.StartOffset(), |
michael@0 | 2804 | offsets.EndOffset(), false, false, |
michael@0 | 2805 | offsets.associateWithNext); |
michael@0 | 2806 | } |
michael@0 | 2807 | |
michael@0 | 2808 | // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation |
michael@0 | 2809 | nsCOMPtr<nsIContent>parentContent; |
michael@0 | 2810 | int32_t contentOffset; |
michael@0 | 2811 | int32_t target; |
michael@0 | 2812 | rv = GetDataForTableSelection(frameselection, shell, mouseEvent, |
michael@0 | 2813 | getter_AddRefs(parentContent), &contentOffset, |
michael@0 | 2814 | &target); |
michael@0 | 2815 | if (NS_SUCCEEDED(rv) && parentContent) |
michael@0 | 2816 | { |
michael@0 | 2817 | fc->SetMouseDownState(true); |
michael@0 | 2818 | return fc->HandleTableSelection(parentContent, contentOffset, target, |
michael@0 | 2819 | mouseEvent); |
michael@0 | 2820 | } |
michael@0 | 2821 | |
michael@0 | 2822 | fc->SetDelayedCaretData(0); |
michael@0 | 2823 | |
michael@0 | 2824 | // Check if any part of this frame is selected, and if the |
michael@0 | 2825 | // user clicked inside the selected region. If so, we delay |
michael@0 | 2826 | // starting a new selection since the user may be trying to |
michael@0 | 2827 | // drag the selected region to some other app. |
michael@0 | 2828 | |
michael@0 | 2829 | SelectionDetails *details = 0; |
michael@0 | 2830 | if (GetContent()->IsSelectionDescendant()) |
michael@0 | 2831 | { |
michael@0 | 2832 | bool inSelection = false; |
michael@0 | 2833 | details = frameselection->LookUpSelection(offsets.content, 0, |
michael@0 | 2834 | offsets.EndOffset(), false); |
michael@0 | 2835 | |
michael@0 | 2836 | // |
michael@0 | 2837 | // If there are any details, check to see if the user clicked |
michael@0 | 2838 | // within any selected region of the frame. |
michael@0 | 2839 | // |
michael@0 | 2840 | |
michael@0 | 2841 | SelectionDetails *curDetail = details; |
michael@0 | 2842 | |
michael@0 | 2843 | while (curDetail) |
michael@0 | 2844 | { |
michael@0 | 2845 | // |
michael@0 | 2846 | // If the user clicked inside a selection, then just |
michael@0 | 2847 | // return without doing anything. We will handle placing |
michael@0 | 2848 | // the caret later on when the mouse is released. We ignore |
michael@0 | 2849 | // the spellcheck, find and url formatting selections. |
michael@0 | 2850 | // |
michael@0 | 2851 | if (curDetail->mType != nsISelectionController::SELECTION_SPELLCHECK && |
michael@0 | 2852 | curDetail->mType != nsISelectionController::SELECTION_FIND && |
michael@0 | 2853 | curDetail->mType != nsISelectionController::SELECTION_URLSECONDARY && |
michael@0 | 2854 | curDetail->mStart <= offsets.StartOffset() && |
michael@0 | 2855 | offsets.EndOffset() <= curDetail->mEnd) |
michael@0 | 2856 | { |
michael@0 | 2857 | inSelection = true; |
michael@0 | 2858 | } |
michael@0 | 2859 | |
michael@0 | 2860 | SelectionDetails *nextDetail = curDetail->mNext; |
michael@0 | 2861 | delete curDetail; |
michael@0 | 2862 | curDetail = nextDetail; |
michael@0 | 2863 | } |
michael@0 | 2864 | |
michael@0 | 2865 | if (inSelection) { |
michael@0 | 2866 | fc->SetMouseDownState(false); |
michael@0 | 2867 | fc->SetDelayedCaretData(mouseEvent); |
michael@0 | 2868 | return NS_OK; |
michael@0 | 2869 | } |
michael@0 | 2870 | } |
michael@0 | 2871 | |
michael@0 | 2872 | fc->SetMouseDownState(true); |
michael@0 | 2873 | |
michael@0 | 2874 | // Do not touch any nsFrame members after this point without adding |
michael@0 | 2875 | // weakFrame checks. |
michael@0 | 2876 | rv = fc->HandleClick(offsets.content, offsets.StartOffset(), |
michael@0 | 2877 | offsets.EndOffset(), mouseEvent->IsShift(), control, |
michael@0 | 2878 | offsets.associateWithNext); |
michael@0 | 2879 | |
michael@0 | 2880 | if (NS_FAILED(rv)) |
michael@0 | 2881 | return rv; |
michael@0 | 2882 | |
michael@0 | 2883 | if (offsets.offset != offsets.secondaryOffset) |
michael@0 | 2884 | fc->MaintainSelection(); |
michael@0 | 2885 | |
michael@0 | 2886 | if (isEditor && !mouseEvent->IsShift() && |
michael@0 | 2887 | (offsets.EndOffset() - offsets.StartOffset()) == 1) |
michael@0 | 2888 | { |
michael@0 | 2889 | // A single node is selected and we aren't extending an existing |
michael@0 | 2890 | // selection, which means the user clicked directly on an object (either |
michael@0 | 2891 | // -moz-user-select: all or a non-text node without children). |
michael@0 | 2892 | // Therefore, disable selection extension during mouse moves. |
michael@0 | 2893 | // XXX This is a bit hacky; shouldn't editor be able to deal with this? |
michael@0 | 2894 | fc->SetMouseDownState(false); |
michael@0 | 2895 | } |
michael@0 | 2896 | |
michael@0 | 2897 | return rv; |
michael@0 | 2898 | } |
michael@0 | 2899 | |
michael@0 | 2900 | /* |
michael@0 | 2901 | * SelectByTypeAtPoint |
michael@0 | 2902 | * |
michael@0 | 2903 | * Search for selectable content at point and attempt to select |
michael@0 | 2904 | * based on the start and end selection behaviours. |
michael@0 | 2905 | * |
michael@0 | 2906 | * @param aPresContext Presentation context |
michael@0 | 2907 | * @param aPoint Point at which selection will occur. Coordinates |
michael@0 | 2908 | * should be relaitve to this frame. |
michael@0 | 2909 | * @param aBeginAmountType, aEndAmountType Selection behavior, see |
michael@0 | 2910 | * nsIFrame for definitions. |
michael@0 | 2911 | * @param aSelectFlags Selection flags defined in nsFame.h. |
michael@0 | 2912 | * @return success or failure at finding suitable content to select. |
michael@0 | 2913 | */ |
michael@0 | 2914 | nsresult |
michael@0 | 2915 | nsFrame::SelectByTypeAtPoint(nsPresContext* aPresContext, |
michael@0 | 2916 | const nsPoint& aPoint, |
michael@0 | 2917 | nsSelectionAmount aBeginAmountType, |
michael@0 | 2918 | nsSelectionAmount aEndAmountType, |
michael@0 | 2919 | uint32_t aSelectFlags) |
michael@0 | 2920 | { |
michael@0 | 2921 | NS_ENSURE_ARG_POINTER(aPresContext); |
michael@0 | 2922 | |
michael@0 | 2923 | // No point in selecting if selection is turned off |
michael@0 | 2924 | if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) |
michael@0 | 2925 | return NS_OK; |
michael@0 | 2926 | |
michael@0 | 2927 | ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN); |
michael@0 | 2928 | if (!offsets.content) |
michael@0 | 2929 | return NS_ERROR_FAILURE; |
michael@0 | 2930 | |
michael@0 | 2931 | int32_t offset; |
michael@0 | 2932 | const nsFrameSelection* frameSelection = |
michael@0 | 2933 | PresContext()->GetPresShell()->ConstFrameSelection(); |
michael@0 | 2934 | nsIFrame* theFrame = frameSelection-> |
michael@0 | 2935 | GetFrameForNodeOffset(offsets.content, offsets.offset, |
michael@0 | 2936 | nsFrameSelection::HINT(offsets.associateWithNext), |
michael@0 | 2937 | &offset); |
michael@0 | 2938 | if (!theFrame) |
michael@0 | 2939 | return NS_ERROR_FAILURE; |
michael@0 | 2940 | |
michael@0 | 2941 | nsFrame* frame = static_cast<nsFrame*>(theFrame); |
michael@0 | 2942 | return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, |
michael@0 | 2943 | offset, aPresContext, |
michael@0 | 2944 | aBeginAmountType != eSelectWord, |
michael@0 | 2945 | aSelectFlags); |
michael@0 | 2946 | } |
michael@0 | 2947 | |
michael@0 | 2948 | /** |
michael@0 | 2949 | * Multiple Mouse Press -- line or paragraph selection -- for the frame. |
michael@0 | 2950 | * Wouldn't it be nice if this didn't have to be hardwired into Frame code? |
michael@0 | 2951 | */ |
michael@0 | 2952 | NS_IMETHODIMP |
michael@0 | 2953 | nsFrame::HandleMultiplePress(nsPresContext* aPresContext, |
michael@0 | 2954 | WidgetGUIEvent* aEvent, |
michael@0 | 2955 | nsEventStatus* aEventStatus, |
michael@0 | 2956 | bool aControlHeld) |
michael@0 | 2957 | { |
michael@0 | 2958 | NS_ENSURE_ARG_POINTER(aEvent); |
michael@0 | 2959 | NS_ENSURE_ARG_POINTER(aEventStatus); |
michael@0 | 2960 | |
michael@0 | 2961 | if (nsEventStatus_eConsumeNoDefault == *aEventStatus || |
michael@0 | 2962 | DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { |
michael@0 | 2963 | return NS_OK; |
michael@0 | 2964 | } |
michael@0 | 2965 | |
michael@0 | 2966 | // Find out whether we're doing line or paragraph selection. |
michael@0 | 2967 | // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph. |
michael@0 | 2968 | // Otherwise, triple-click selects line, and quadruple-click selects paragraph |
michael@0 | 2969 | // (on platforms that support quadruple-click). |
michael@0 | 2970 | nsSelectionAmount beginAmount, endAmount; |
michael@0 | 2971 | WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
michael@0 | 2972 | if (!mouseEvent) { |
michael@0 | 2973 | return NS_OK; |
michael@0 | 2974 | } |
michael@0 | 2975 | |
michael@0 | 2976 | if (mouseEvent->clickCount == 4) { |
michael@0 | 2977 | beginAmount = endAmount = eSelectParagraph; |
michael@0 | 2978 | } else if (mouseEvent->clickCount == 3) { |
michael@0 | 2979 | if (Preferences::GetBool("browser.triple_click_selects_paragraph")) { |
michael@0 | 2980 | beginAmount = endAmount = eSelectParagraph; |
michael@0 | 2981 | } else { |
michael@0 | 2982 | beginAmount = eSelectBeginLine; |
michael@0 | 2983 | endAmount = eSelectEndLine; |
michael@0 | 2984 | } |
michael@0 | 2985 | } else if (mouseEvent->clickCount == 2) { |
michael@0 | 2986 | // We only want inline frames; PeekBackwardAndForward dislikes blocks |
michael@0 | 2987 | beginAmount = endAmount = eSelectWord; |
michael@0 | 2988 | } else { |
michael@0 | 2989 | return NS_OK; |
michael@0 | 2990 | } |
michael@0 | 2991 | |
michael@0 | 2992 | nsPoint relPoint = |
michael@0 | 2993 | nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); |
michael@0 | 2994 | return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount, |
michael@0 | 2995 | (aControlHeld ? SELECT_ACCUMULATE : 0)); |
michael@0 | 2996 | } |
michael@0 | 2997 | |
michael@0 | 2998 | nsresult |
michael@0 | 2999 | nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, |
michael@0 | 3000 | nsSelectionAmount aAmountForward, |
michael@0 | 3001 | int32_t aStartPos, |
michael@0 | 3002 | nsPresContext* aPresContext, |
michael@0 | 3003 | bool aJumpLines, |
michael@0 | 3004 | uint32_t aSelectFlags) |
michael@0 | 3005 | { |
michael@0 | 3006 | nsIFrame* baseFrame = this; |
michael@0 | 3007 | int32_t baseOffset = aStartPos; |
michael@0 | 3008 | nsresult rv; |
michael@0 | 3009 | |
michael@0 | 3010 | if (aAmountBack == eSelectWord) { |
michael@0 | 3011 | // To avoid selecting the previous word when at start of word, |
michael@0 | 3012 | // first move one character forward. |
michael@0 | 3013 | nsPeekOffsetStruct pos(eSelectCharacter, |
michael@0 | 3014 | eDirNext, |
michael@0 | 3015 | aStartPos, |
michael@0 | 3016 | 0, |
michael@0 | 3017 | aJumpLines, |
michael@0 | 3018 | true, //limit on scrolled views |
michael@0 | 3019 | false, |
michael@0 | 3020 | false); |
michael@0 | 3021 | rv = PeekOffset(&pos); |
michael@0 | 3022 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 3023 | baseFrame = pos.mResultFrame; |
michael@0 | 3024 | baseOffset = pos.mContentOffset; |
michael@0 | 3025 | } |
michael@0 | 3026 | } |
michael@0 | 3027 | |
michael@0 | 3028 | // Use peek offset one way then the other: |
michael@0 | 3029 | nsPeekOffsetStruct startpos(aAmountBack, |
michael@0 | 3030 | eDirPrevious, |
michael@0 | 3031 | baseOffset, |
michael@0 | 3032 | 0, |
michael@0 | 3033 | aJumpLines, |
michael@0 | 3034 | true, //limit on scrolled views |
michael@0 | 3035 | false, |
michael@0 | 3036 | false); |
michael@0 | 3037 | rv = baseFrame->PeekOffset(&startpos); |
michael@0 | 3038 | if (NS_FAILED(rv)) |
michael@0 | 3039 | return rv; |
michael@0 | 3040 | |
michael@0 | 3041 | nsPeekOffsetStruct endpos(aAmountForward, |
michael@0 | 3042 | eDirNext, |
michael@0 | 3043 | aStartPos, |
michael@0 | 3044 | 0, |
michael@0 | 3045 | aJumpLines, |
michael@0 | 3046 | true, //limit on scrolled views |
michael@0 | 3047 | false, |
michael@0 | 3048 | false); |
michael@0 | 3049 | rv = PeekOffset(&endpos); |
michael@0 | 3050 | if (NS_FAILED(rv)) |
michael@0 | 3051 | return rv; |
michael@0 | 3052 | |
michael@0 | 3053 | // Keep frameSelection alive. |
michael@0 | 3054 | nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); |
michael@0 | 3055 | |
michael@0 | 3056 | rv = frameSelection->HandleClick(startpos.mResultContent, |
michael@0 | 3057 | startpos.mContentOffset, startpos.mContentOffset, |
michael@0 | 3058 | false, (aSelectFlags & SELECT_ACCUMULATE), |
michael@0 | 3059 | nsFrameSelection::HINTRIGHT); |
michael@0 | 3060 | if (NS_FAILED(rv)) |
michael@0 | 3061 | return rv; |
michael@0 | 3062 | |
michael@0 | 3063 | rv = frameSelection->HandleClick(endpos.mResultContent, |
michael@0 | 3064 | endpos.mContentOffset, endpos.mContentOffset, |
michael@0 | 3065 | true, false, |
michael@0 | 3066 | nsFrameSelection::HINTLEFT); |
michael@0 | 3067 | if (NS_FAILED(rv)) |
michael@0 | 3068 | return rv; |
michael@0 | 3069 | |
michael@0 | 3070 | // maintain selection |
michael@0 | 3071 | return frameSelection->MaintainSelection(aAmountBack); |
michael@0 | 3072 | } |
michael@0 | 3073 | |
michael@0 | 3074 | NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, |
michael@0 | 3075 | WidgetGUIEvent* aEvent, |
michael@0 | 3076 | nsEventStatus* aEventStatus) |
michael@0 | 3077 | { |
michael@0 | 3078 | MOZ_ASSERT(aEvent->eventStructType == NS_MOUSE_EVENT, "HandleDrag can only handle mouse event"); |
michael@0 | 3079 | |
michael@0 | 3080 | bool selectable; |
michael@0 | 3081 | IsSelectable(&selectable, nullptr); |
michael@0 | 3082 | |
michael@0 | 3083 | // XXX Do we really need to exclude non-selectable content here? |
michael@0 | 3084 | // GetContentOffsetsFromPoint can handle it just fine, although some |
michael@0 | 3085 | // other stuff might not like it. |
michael@0 | 3086 | if (!selectable) |
michael@0 | 3087 | return NS_OK; |
michael@0 | 3088 | if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { |
michael@0 | 3089 | return NS_OK; |
michael@0 | 3090 | } |
michael@0 | 3091 | nsIPresShell *presShell = aPresContext->PresShell(); |
michael@0 | 3092 | |
michael@0 | 3093 | nsRefPtr<nsFrameSelection> frameselection = GetFrameSelection(); |
michael@0 | 3094 | bool mouseDown = frameselection->GetMouseDownState(); |
michael@0 | 3095 | if (!mouseDown) |
michael@0 | 3096 | return NS_OK; |
michael@0 | 3097 | |
michael@0 | 3098 | frameselection->StopAutoScrollTimer(); |
michael@0 | 3099 | |
michael@0 | 3100 | // Check if we are dragging in a table cell |
michael@0 | 3101 | nsCOMPtr<nsIContent> parentContent; |
michael@0 | 3102 | int32_t contentOffset; |
michael@0 | 3103 | int32_t target; |
michael@0 | 3104 | WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
michael@0 | 3105 | nsresult result; |
michael@0 | 3106 | result = GetDataForTableSelection(frameselection, presShell, mouseEvent, |
michael@0 | 3107 | getter_AddRefs(parentContent), |
michael@0 | 3108 | &contentOffset, &target); |
michael@0 | 3109 | |
michael@0 | 3110 | nsWeakFrame weakThis = this; |
michael@0 | 3111 | if (NS_SUCCEEDED(result) && parentContent) { |
michael@0 | 3112 | frameselection->HandleTableSelection(parentContent, contentOffset, target, |
michael@0 | 3113 | mouseEvent); |
michael@0 | 3114 | } else { |
michael@0 | 3115 | nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); |
michael@0 | 3116 | frameselection->HandleDrag(this, pt); |
michael@0 | 3117 | } |
michael@0 | 3118 | |
michael@0 | 3119 | // The frameselection object notifies selection listeners synchronously above |
michael@0 | 3120 | // which might have killed us. |
michael@0 | 3121 | if (!weakThis.IsAlive()) { |
michael@0 | 3122 | return NS_OK; |
michael@0 | 3123 | } |
michael@0 | 3124 | |
michael@0 | 3125 | // get the nearest scrollframe |
michael@0 | 3126 | nsIScrollableFrame* scrollFrame = |
michael@0 | 3127 | nsLayoutUtils::GetNearestScrollableFrame(this, |
michael@0 | 3128 | nsLayoutUtils::SCROLLABLE_SAME_DOC | |
michael@0 | 3129 | nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); |
michael@0 | 3130 | |
michael@0 | 3131 | if (scrollFrame) { |
michael@0 | 3132 | nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame(); |
michael@0 | 3133 | if (capturingFrame) { |
michael@0 | 3134 | nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, |
michael@0 | 3135 | capturingFrame); |
michael@0 | 3136 | frameselection->StartAutoScrollTimer(capturingFrame, pt, 30); |
michael@0 | 3137 | } |
michael@0 | 3138 | } |
michael@0 | 3139 | |
michael@0 | 3140 | return NS_OK; |
michael@0 | 3141 | } |
michael@0 | 3142 | |
michael@0 | 3143 | /** |
michael@0 | 3144 | * This static method handles part of the nsFrame::HandleRelease in a way |
michael@0 | 3145 | * which doesn't rely on the nsFrame object to stay alive. |
michael@0 | 3146 | */ |
michael@0 | 3147 | static nsresult |
michael@0 | 3148 | HandleFrameSelection(nsFrameSelection* aFrameSelection, |
michael@0 | 3149 | nsIFrame::ContentOffsets& aOffsets, |
michael@0 | 3150 | bool aHandleTableSel, |
michael@0 | 3151 | int32_t aContentOffsetForTableSel, |
michael@0 | 3152 | int32_t aTargetForTableSel, |
michael@0 | 3153 | nsIContent* aParentContentForTableSel, |
michael@0 | 3154 | WidgetGUIEvent* aEvent, |
michael@0 | 3155 | nsEventStatus* aEventStatus) |
michael@0 | 3156 | { |
michael@0 | 3157 | if (!aFrameSelection) { |
michael@0 | 3158 | return NS_OK; |
michael@0 | 3159 | } |
michael@0 | 3160 | |
michael@0 | 3161 | nsresult rv = NS_OK; |
michael@0 | 3162 | |
michael@0 | 3163 | if (nsEventStatus_eConsumeNoDefault != *aEventStatus) { |
michael@0 | 3164 | if (!aHandleTableSel) { |
michael@0 | 3165 | if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) { |
michael@0 | 3166 | return NS_ERROR_FAILURE; |
michael@0 | 3167 | } |
michael@0 | 3168 | |
michael@0 | 3169 | // We are doing this to simulate what we would have done on HandlePress. |
michael@0 | 3170 | // We didn't do it there to give the user an opportunity to drag |
michael@0 | 3171 | // the text, but since they didn't drag, we want to place the |
michael@0 | 3172 | // caret. |
michael@0 | 3173 | // However, we'll use the mouse position from the release, since: |
michael@0 | 3174 | // * it's easier |
michael@0 | 3175 | // * that's the normal click position to use (although really, in |
michael@0 | 3176 | // the normal case, small movements that don't count as a drag |
michael@0 | 3177 | // can do selection) |
michael@0 | 3178 | aFrameSelection->SetMouseDownState(true); |
michael@0 | 3179 | |
michael@0 | 3180 | rv = aFrameSelection->HandleClick(aOffsets.content, |
michael@0 | 3181 | aOffsets.StartOffset(), |
michael@0 | 3182 | aOffsets.EndOffset(), |
michael@0 | 3183 | aFrameSelection->IsShiftDownInDelayedCaretData(), |
michael@0 | 3184 | false, |
michael@0 | 3185 | aOffsets.associateWithNext); |
michael@0 | 3186 | if (NS_FAILED(rv)) { |
michael@0 | 3187 | return rv; |
michael@0 | 3188 | } |
michael@0 | 3189 | } else if (aParentContentForTableSel) { |
michael@0 | 3190 | aFrameSelection->SetMouseDownState(false); |
michael@0 | 3191 | rv = aFrameSelection->HandleTableSelection( |
michael@0 | 3192 | aParentContentForTableSel, |
michael@0 | 3193 | aContentOffsetForTableSel, |
michael@0 | 3194 | aTargetForTableSel, |
michael@0 | 3195 | aEvent->AsMouseEvent()); |
michael@0 | 3196 | if (NS_FAILED(rv)) { |
michael@0 | 3197 | return rv; |
michael@0 | 3198 | } |
michael@0 | 3199 | } |
michael@0 | 3200 | aFrameSelection->SetDelayedCaretData(0); |
michael@0 | 3201 | } |
michael@0 | 3202 | |
michael@0 | 3203 | aFrameSelection->SetMouseDownState(false); |
michael@0 | 3204 | aFrameSelection->StopAutoScrollTimer(); |
michael@0 | 3205 | |
michael@0 | 3206 | return NS_OK; |
michael@0 | 3207 | } |
michael@0 | 3208 | |
michael@0 | 3209 | NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext, |
michael@0 | 3210 | WidgetGUIEvent* aEvent, |
michael@0 | 3211 | nsEventStatus* aEventStatus) |
michael@0 | 3212 | { |
michael@0 | 3213 | if (aEvent->eventStructType != NS_MOUSE_EVENT) { |
michael@0 | 3214 | return NS_OK; |
michael@0 | 3215 | } |
michael@0 | 3216 | |
michael@0 | 3217 | nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this); |
michael@0 | 3218 | |
michael@0 | 3219 | nsCOMPtr<nsIContent> captureContent = nsIPresShell::GetCapturingContent(); |
michael@0 | 3220 | |
michael@0 | 3221 | // We can unconditionally stop capturing because |
michael@0 | 3222 | // we should never be capturing when the mouse button is up |
michael@0 | 3223 | nsIPresShell::SetCapturingContent(nullptr, 0); |
michael@0 | 3224 | |
michael@0 | 3225 | bool selectionOff = |
michael@0 | 3226 | (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF); |
michael@0 | 3227 | |
michael@0 | 3228 | nsRefPtr<nsFrameSelection> frameselection; |
michael@0 | 3229 | ContentOffsets offsets; |
michael@0 | 3230 | nsCOMPtr<nsIContent> parentContent; |
michael@0 | 3231 | int32_t contentOffsetForTableSel = 0; |
michael@0 | 3232 | int32_t targetForTableSel = 0; |
michael@0 | 3233 | bool handleTableSelection = true; |
michael@0 | 3234 | |
michael@0 | 3235 | if (!selectionOff) { |
michael@0 | 3236 | frameselection = GetFrameSelection(); |
michael@0 | 3237 | if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) { |
michael@0 | 3238 | // Check if the frameselection recorded the mouse going down. |
michael@0 | 3239 | // If not, the user must have clicked in a part of the selection. |
michael@0 | 3240 | // Place the caret before continuing! |
michael@0 | 3241 | |
michael@0 | 3242 | bool mouseDown = frameselection->GetMouseDownState(); |
michael@0 | 3243 | |
michael@0 | 3244 | if (!mouseDown && frameselection->HasDelayedCaretData() && |
michael@0 | 3245 | frameselection->GetClickCountInDelayedCaretData() < 2) { |
michael@0 | 3246 | nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); |
michael@0 | 3247 | offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN); |
michael@0 | 3248 | handleTableSelection = false; |
michael@0 | 3249 | } else { |
michael@0 | 3250 | GetDataForTableSelection(frameselection, PresContext()->PresShell(), |
michael@0 | 3251 | aEvent->AsMouseEvent(), |
michael@0 | 3252 | getter_AddRefs(parentContent), |
michael@0 | 3253 | &contentOffsetForTableSel, |
michael@0 | 3254 | &targetForTableSel); |
michael@0 | 3255 | } |
michael@0 | 3256 | } |
michael@0 | 3257 | } |
michael@0 | 3258 | |
michael@0 | 3259 | // We might be capturing in some other document and the event just happened to |
michael@0 | 3260 | // trickle down here. Make sure that document's frame selection is notified. |
michael@0 | 3261 | // Note, this may cause the current nsFrame object to be deleted, bug 336592. |
michael@0 | 3262 | nsRefPtr<nsFrameSelection> frameSelection; |
michael@0 | 3263 | if (activeFrame != this && |
michael@0 | 3264 | static_cast<nsFrame*>(activeFrame)->DisplaySelection(activeFrame->PresContext()) |
michael@0 | 3265 | != nsISelectionController::SELECTION_OFF) { |
michael@0 | 3266 | frameSelection = activeFrame->GetFrameSelection(); |
michael@0 | 3267 | } |
michael@0 | 3268 | |
michael@0 | 3269 | // Also check the selection of the capturing content which might be in a |
michael@0 | 3270 | // different document. |
michael@0 | 3271 | if (!frameSelection && captureContent) { |
michael@0 | 3272 | nsIDocument* doc = captureContent->GetCurrentDoc(); |
michael@0 | 3273 | if (doc) { |
michael@0 | 3274 | nsIPresShell* capturingShell = doc->GetShell(); |
michael@0 | 3275 | if (capturingShell && capturingShell != PresContext()->GetPresShell()) { |
michael@0 | 3276 | frameSelection = capturingShell->FrameSelection(); |
michael@0 | 3277 | } |
michael@0 | 3278 | } |
michael@0 | 3279 | } |
michael@0 | 3280 | |
michael@0 | 3281 | if (frameSelection) { |
michael@0 | 3282 | frameSelection->SetMouseDownState(false); |
michael@0 | 3283 | frameSelection->StopAutoScrollTimer(); |
michael@0 | 3284 | } |
michael@0 | 3285 | |
michael@0 | 3286 | // Do not call any methods of the current object after this point!!! |
michael@0 | 3287 | // The object is perhaps dead! |
michael@0 | 3288 | |
michael@0 | 3289 | return selectionOff |
michael@0 | 3290 | ? NS_OK |
michael@0 | 3291 | : HandleFrameSelection(frameselection, offsets, handleTableSelection, |
michael@0 | 3292 | contentOffsetForTableSel, targetForTableSel, |
michael@0 | 3293 | parentContent, aEvent, aEventStatus); |
michael@0 | 3294 | } |
michael@0 | 3295 | |
michael@0 | 3296 | struct MOZ_STACK_CLASS FrameContentRange { |
michael@0 | 3297 | FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd) : |
michael@0 | 3298 | content(aContent), start(aStart), end(aEnd) { } |
michael@0 | 3299 | nsCOMPtr<nsIContent> content; |
michael@0 | 3300 | int32_t start; |
michael@0 | 3301 | int32_t end; |
michael@0 | 3302 | }; |
michael@0 | 3303 | |
michael@0 | 3304 | // Retrieve the content offsets of a frame |
michael@0 | 3305 | static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) { |
michael@0 | 3306 | nsCOMPtr<nsIContent> content, parent; |
michael@0 | 3307 | content = aFrame->GetContent(); |
michael@0 | 3308 | if (!content) { |
michael@0 | 3309 | NS_WARNING("Frame has no content"); |
michael@0 | 3310 | return FrameContentRange(nullptr, -1, -1); |
michael@0 | 3311 | } |
michael@0 | 3312 | nsIAtom* type = aFrame->GetType(); |
michael@0 | 3313 | if (type == nsGkAtoms::textFrame) { |
michael@0 | 3314 | int32_t offset, offsetEnd; |
michael@0 | 3315 | aFrame->GetOffsets(offset, offsetEnd); |
michael@0 | 3316 | return FrameContentRange(content, offset, offsetEnd); |
michael@0 | 3317 | } |
michael@0 | 3318 | if (type == nsGkAtoms::brFrame) { |
michael@0 | 3319 | parent = content->GetParent(); |
michael@0 | 3320 | int32_t beginOffset = parent->IndexOf(content); |
michael@0 | 3321 | return FrameContentRange(parent, beginOffset, beginOffset); |
michael@0 | 3322 | } |
michael@0 | 3323 | // Loop to deal with anonymous content, which has no index; this loop |
michael@0 | 3324 | // probably won't run more than twice under normal conditions |
michael@0 | 3325 | do { |
michael@0 | 3326 | parent = content->GetParent(); |
michael@0 | 3327 | if (parent) { |
michael@0 | 3328 | int32_t beginOffset = parent->IndexOf(content); |
michael@0 | 3329 | if (beginOffset >= 0) |
michael@0 | 3330 | return FrameContentRange(parent, beginOffset, beginOffset + 1); |
michael@0 | 3331 | content = parent; |
michael@0 | 3332 | } |
michael@0 | 3333 | } while (parent); |
michael@0 | 3334 | |
michael@0 | 3335 | // The root content node must act differently |
michael@0 | 3336 | return FrameContentRange(content, 0, content->GetChildCount()); |
michael@0 | 3337 | } |
michael@0 | 3338 | |
michael@0 | 3339 | // The FrameTarget represents the closest frame to a point that can be selected |
michael@0 | 3340 | // The frame is the frame represented, frameEdge says whether one end of the |
michael@0 | 3341 | // frame is the result (in which case different handling is needed), and |
michael@0 | 3342 | // afterFrame says which end is repersented if frameEdge is true |
michael@0 | 3343 | struct FrameTarget { |
michael@0 | 3344 | FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame, |
michael@0 | 3345 | bool aEmptyBlock = false) : |
michael@0 | 3346 | frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame), |
michael@0 | 3347 | emptyBlock(aEmptyBlock) { } |
michael@0 | 3348 | static FrameTarget Null() { |
michael@0 | 3349 | return FrameTarget(nullptr, false, false); |
michael@0 | 3350 | } |
michael@0 | 3351 | bool IsNull() { |
michael@0 | 3352 | return !frame; |
michael@0 | 3353 | } |
michael@0 | 3354 | nsIFrame* frame; |
michael@0 | 3355 | bool frameEdge; |
michael@0 | 3356 | bool afterFrame; |
michael@0 | 3357 | bool emptyBlock; |
michael@0 | 3358 | }; |
michael@0 | 3359 | |
michael@0 | 3360 | // See function implementation for information |
michael@0 | 3361 | static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint, |
michael@0 | 3362 | uint32_t aFlags); |
michael@0 | 3363 | |
michael@0 | 3364 | static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) |
michael@0 | 3365 | { |
michael@0 | 3366 | if ((aFlags & nsIFrame::SKIP_HIDDEN) && |
michael@0 | 3367 | !aFrame->StyleVisibility()->IsVisible()) { |
michael@0 | 3368 | return false; |
michael@0 | 3369 | } |
michael@0 | 3370 | return !aFrame->IsGeneratedContentFrame() && |
michael@0 | 3371 | aFrame->StyleUIReset()->mUserSelect != NS_STYLE_USER_SELECT_NONE; |
michael@0 | 3372 | } |
michael@0 | 3373 | |
michael@0 | 3374 | static bool SelectionDescendToKids(nsIFrame* aFrame) { |
michael@0 | 3375 | uint8_t style = aFrame->StyleUIReset()->mUserSelect; |
michael@0 | 3376 | nsIFrame* parent = aFrame->GetParent(); |
michael@0 | 3377 | // If we are only near (not directly over) then don't traverse |
michael@0 | 3378 | // frames with independent selection (e.g. text and list controls) |
michael@0 | 3379 | // unless we're already inside such a frame (see bug 268497). Note that this |
michael@0 | 3380 | // prevents any of the users of this method from entering form controls. |
michael@0 | 3381 | // XXX We might want some way to allow using the up-arrow to go into a form |
michael@0 | 3382 | // control, but the focus didn't work right anyway; it'd probably be enough |
michael@0 | 3383 | // if the left and right arrows could enter textboxes (which I don't believe |
michael@0 | 3384 | // they can at the moment) |
michael@0 | 3385 | return !aFrame->IsGeneratedContentFrame() && |
michael@0 | 3386 | style != NS_STYLE_USER_SELECT_ALL && |
michael@0 | 3387 | style != NS_STYLE_USER_SELECT_NONE && |
michael@0 | 3388 | ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) || |
michael@0 | 3389 | !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)); |
michael@0 | 3390 | } |
michael@0 | 3391 | |
michael@0 | 3392 | static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild, |
michael@0 | 3393 | nsPoint aPoint, |
michael@0 | 3394 | uint32_t aFlags) |
michael@0 | 3395 | { |
michael@0 | 3396 | nsIFrame* parent = aChild->GetParent(); |
michael@0 | 3397 | if (SelectionDescendToKids(aChild)) { |
michael@0 | 3398 | nsPoint pt = aPoint - aChild->GetOffsetTo(parent); |
michael@0 | 3399 | return GetSelectionClosestFrame(aChild, pt, aFlags); |
michael@0 | 3400 | } |
michael@0 | 3401 | return FrameTarget(aChild, false, false); |
michael@0 | 3402 | } |
michael@0 | 3403 | |
michael@0 | 3404 | // When the cursor needs to be at the beginning of a block, it shouldn't be |
michael@0 | 3405 | // before the first child. A click on a block whose first child is a block |
michael@0 | 3406 | // should put the cursor in the child. The cursor shouldn't be between the |
michael@0 | 3407 | // blocks, because that's not where it's expected. |
michael@0 | 3408 | // Note that this method is guaranteed to succeed. |
michael@0 | 3409 | static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, |
michael@0 | 3410 | bool aEndFrame, uint32_t aFlags) { |
michael@0 | 3411 | if (SelectionDescendToKids(aFrame)) { |
michael@0 | 3412 | nsIFrame* result = nullptr; |
michael@0 | 3413 | nsIFrame *frame = aFrame->GetFirstPrincipalChild(); |
michael@0 | 3414 | if (!aEndFrame) { |
michael@0 | 3415 | while (frame && (!SelfIsSelectable(frame, aFlags) || |
michael@0 | 3416 | frame->IsEmpty())) |
michael@0 | 3417 | frame = frame->GetNextSibling(); |
michael@0 | 3418 | if (frame) |
michael@0 | 3419 | result = frame; |
michael@0 | 3420 | } else { |
michael@0 | 3421 | // Because the frame tree is singly linked, to find the last frame, |
michael@0 | 3422 | // we have to iterate through all the frames |
michael@0 | 3423 | // XXX I have a feeling this could be slow for long blocks, although |
michael@0 | 3424 | // I can't find any slowdowns |
michael@0 | 3425 | while (frame) { |
michael@0 | 3426 | if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags)) |
michael@0 | 3427 | result = frame; |
michael@0 | 3428 | frame = frame->GetNextSibling(); |
michael@0 | 3429 | } |
michael@0 | 3430 | } |
michael@0 | 3431 | if (result) |
michael@0 | 3432 | return DrillDownToSelectionFrame(result, aEndFrame, aFlags); |
michael@0 | 3433 | } |
michael@0 | 3434 | // If the current frame has no targetable children, target the current frame |
michael@0 | 3435 | return FrameTarget(aFrame, true, aEndFrame); |
michael@0 | 3436 | } |
michael@0 | 3437 | |
michael@0 | 3438 | // This method finds the closest valid FrameTarget on a given line; if there is |
michael@0 | 3439 | // no valid FrameTarget on the line, it returns a null FrameTarget |
michael@0 | 3440 | static FrameTarget GetSelectionClosestFrameForLine( |
michael@0 | 3441 | nsBlockFrame* aParent, |
michael@0 | 3442 | nsBlockFrame::line_iterator aLine, |
michael@0 | 3443 | nsPoint aPoint, |
michael@0 | 3444 | uint32_t aFlags) |
michael@0 | 3445 | { |
michael@0 | 3446 | nsIFrame *frame = aLine->mFirstChild; |
michael@0 | 3447 | // Account for end of lines (any iterator from the block is valid) |
michael@0 | 3448 | if (aLine == aParent->end_lines()) |
michael@0 | 3449 | return DrillDownToSelectionFrame(aParent, true, aFlags); |
michael@0 | 3450 | nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr; |
michael@0 | 3451 | nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd(); |
michael@0 | 3452 | WritingMode wm = aLine->mWritingMode; |
michael@0 | 3453 | LogicalPoint pt(wm, aPoint, aLine->mContainerWidth); |
michael@0 | 3454 | for (int32_t n = aLine->GetChildCount(); n; |
michael@0 | 3455 | --n, frame = frame->GetNextSibling()) { |
michael@0 | 3456 | if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()) |
michael@0 | 3457 | continue; |
michael@0 | 3458 | LogicalRect frameRect = LogicalRect(wm, frame->GetRect(), |
michael@0 | 3459 | aLine->mContainerWidth); |
michael@0 | 3460 | if (pt.I(wm) >= frameRect.IStart(wm)) { |
michael@0 | 3461 | if (pt.I(wm) < frameRect.IEnd(wm)) { |
michael@0 | 3462 | return GetSelectionClosestFrameForChild(frame, aPoint, aFlags); |
michael@0 | 3463 | } |
michael@0 | 3464 | if (frameRect.IEnd(wm) >= closestIStart) { |
michael@0 | 3465 | closestFromIStart = frame; |
michael@0 | 3466 | closestIStart = frameRect.IEnd(wm); |
michael@0 | 3467 | } |
michael@0 | 3468 | } else { |
michael@0 | 3469 | if (frameRect.IStart(wm) <= closestIEnd) { |
michael@0 | 3470 | closestFromIEnd = frame; |
michael@0 | 3471 | closestIEnd = frameRect.IStart(wm); |
michael@0 | 3472 | } |
michael@0 | 3473 | } |
michael@0 | 3474 | } |
michael@0 | 3475 | if (!closestFromIStart && !closestFromIEnd) { |
michael@0 | 3476 | // We should only get here if there are no selectable frames on a line |
michael@0 | 3477 | // XXX Do we need more elaborate handling here? |
michael@0 | 3478 | return FrameTarget::Null(); |
michael@0 | 3479 | } |
michael@0 | 3480 | if (closestFromIStart && |
michael@0 | 3481 | (!closestFromIEnd || |
michael@0 | 3482 | (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) { |
michael@0 | 3483 | return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, |
michael@0 | 3484 | aFlags); |
michael@0 | 3485 | } |
michael@0 | 3486 | return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags); |
michael@0 | 3487 | } |
michael@0 | 3488 | |
michael@0 | 3489 | // This method is for the special handling we do for block frames; they're |
michael@0 | 3490 | // special because they represent paragraphs and because they are organized |
michael@0 | 3491 | // into lines, which have bounds that are not stored elsewhere in the |
michael@0 | 3492 | // frame tree. Returns a null FrameTarget for frames which are not |
michael@0 | 3493 | // blocks or blocks with no lines except editable one. |
michael@0 | 3494 | static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame, |
michael@0 | 3495 | nsPoint aPoint, |
michael@0 | 3496 | uint32_t aFlags) |
michael@0 | 3497 | { |
michael@0 | 3498 | nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI |
michael@0 | 3499 | if (!bf) |
michael@0 | 3500 | return FrameTarget::Null(); |
michael@0 | 3501 | |
michael@0 | 3502 | // This code searches for the correct line |
michael@0 | 3503 | nsBlockFrame::line_iterator firstLine = bf->begin_lines(); |
michael@0 | 3504 | nsBlockFrame::line_iterator end = bf->end_lines(); |
michael@0 | 3505 | if (firstLine == end) { |
michael@0 | 3506 | nsIContent *blockContent = aFrame->GetContent(); |
michael@0 | 3507 | if (blockContent) { |
michael@0 | 3508 | // Return with empty flag true. |
michael@0 | 3509 | return FrameTarget(aFrame, false, false, true); |
michael@0 | 3510 | } |
michael@0 | 3511 | return FrameTarget::Null(); |
michael@0 | 3512 | } |
michael@0 | 3513 | nsBlockFrame::line_iterator curLine = firstLine; |
michael@0 | 3514 | nsBlockFrame::line_iterator closestLine = end; |
michael@0 | 3515 | // Convert aPoint into a LogicalPoint in the writing-mode of this block |
michael@0 | 3516 | WritingMode wm = curLine->mWritingMode; |
michael@0 | 3517 | LogicalPoint pt(wm, aPoint, curLine->mContainerWidth); |
michael@0 | 3518 | while (curLine != end) { |
michael@0 | 3519 | // Check to see if our point lies within the line's block-direction bounds |
michael@0 | 3520 | nscoord BCoord = pt.B(wm) - curLine->BStart(); |
michael@0 | 3521 | nscoord BSize = curLine->BSize(); |
michael@0 | 3522 | if (BCoord >= 0 && BCoord < BSize) { |
michael@0 | 3523 | closestLine = curLine; |
michael@0 | 3524 | break; // We found the line; stop looking |
michael@0 | 3525 | } |
michael@0 | 3526 | if (BCoord < 0) |
michael@0 | 3527 | break; |
michael@0 | 3528 | ++curLine; |
michael@0 | 3529 | } |
michael@0 | 3530 | |
michael@0 | 3531 | if (closestLine == end) { |
michael@0 | 3532 | nsBlockFrame::line_iterator prevLine = curLine.prev(); |
michael@0 | 3533 | nsBlockFrame::line_iterator nextLine = curLine; |
michael@0 | 3534 | // Avoid empty lines |
michael@0 | 3535 | while (nextLine != end && nextLine->IsEmpty()) |
michael@0 | 3536 | ++nextLine; |
michael@0 | 3537 | while (prevLine != end && prevLine->IsEmpty()) |
michael@0 | 3538 | --prevLine; |
michael@0 | 3539 | |
michael@0 | 3540 | // This hidden pref dictates whether a point above or below all lines comes |
michael@0 | 3541 | // up with a line or the beginning or end of the frame; 0 on Windows, |
michael@0 | 3542 | // 1 on other platforms by default at the writing of this code |
michael@0 | 3543 | int32_t dragOutOfFrame = |
michael@0 | 3544 | Preferences::GetInt("browser.drag_out_of_frame_style"); |
michael@0 | 3545 | |
michael@0 | 3546 | if (prevLine == end) { |
michael@0 | 3547 | if (dragOutOfFrame == 1 || nextLine == end) |
michael@0 | 3548 | return DrillDownToSelectionFrame(aFrame, false, aFlags); |
michael@0 | 3549 | closestLine = nextLine; |
michael@0 | 3550 | } else if (nextLine == end) { |
michael@0 | 3551 | if (dragOutOfFrame == 1) |
michael@0 | 3552 | return DrillDownToSelectionFrame(aFrame, true, aFlags); |
michael@0 | 3553 | closestLine = prevLine; |
michael@0 | 3554 | } else { // Figure out which line is closer |
michael@0 | 3555 | if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm)) |
michael@0 | 3556 | closestLine = prevLine; |
michael@0 | 3557 | else |
michael@0 | 3558 | closestLine = nextLine; |
michael@0 | 3559 | } |
michael@0 | 3560 | } |
michael@0 | 3561 | |
michael@0 | 3562 | do { |
michael@0 | 3563 | FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine, |
michael@0 | 3564 | aPoint, aFlags); |
michael@0 | 3565 | if (!target.IsNull()) |
michael@0 | 3566 | return target; |
michael@0 | 3567 | ++closestLine; |
michael@0 | 3568 | } while (closestLine != end); |
michael@0 | 3569 | // Fall back to just targeting the last targetable place |
michael@0 | 3570 | return DrillDownToSelectionFrame(aFrame, true, aFlags); |
michael@0 | 3571 | } |
michael@0 | 3572 | |
michael@0 | 3573 | // GetSelectionClosestFrame is the helper function that calculates the closest |
michael@0 | 3574 | // frame to the given point. |
michael@0 | 3575 | // It doesn't completely account for offset styles, so needs to be used in |
michael@0 | 3576 | // restricted environments. |
michael@0 | 3577 | // Cannot handle overlapping frames correctly, so it should receive the output |
michael@0 | 3578 | // of GetFrameForPoint |
michael@0 | 3579 | // Guaranteed to return a valid FrameTarget |
michael@0 | 3580 | static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint, |
michael@0 | 3581 | uint32_t aFlags) |
michael@0 | 3582 | { |
michael@0 | 3583 | { |
michael@0 | 3584 | // Handle blocks; if the frame isn't a block, the method fails |
michael@0 | 3585 | FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags); |
michael@0 | 3586 | if (!target.IsNull()) |
michael@0 | 3587 | return target; |
michael@0 | 3588 | } |
michael@0 | 3589 | |
michael@0 | 3590 | nsIFrame *kid = aFrame->GetFirstPrincipalChild(); |
michael@0 | 3591 | |
michael@0 | 3592 | if (kid) { |
michael@0 | 3593 | // Go through all the child frames to find the closest one |
michael@0 | 3594 | nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX }; |
michael@0 | 3595 | for (; kid; kid = kid->GetNextSibling()) { |
michael@0 | 3596 | if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) |
michael@0 | 3597 | continue; |
michael@0 | 3598 | |
michael@0 | 3599 | kid->FindCloserFrameForSelection(aPoint, &closest); |
michael@0 | 3600 | } |
michael@0 | 3601 | if (closest.mFrame) { |
michael@0 | 3602 | if (closest.mFrame->IsSVGText()) |
michael@0 | 3603 | return FrameTarget(closest.mFrame, false, false); |
michael@0 | 3604 | return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags); |
michael@0 | 3605 | } |
michael@0 | 3606 | } |
michael@0 | 3607 | return FrameTarget(aFrame, false, false); |
michael@0 | 3608 | } |
michael@0 | 3609 | |
michael@0 | 3610 | nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint) |
michael@0 | 3611 | { |
michael@0 | 3612 | nsIFrame::ContentOffsets offsets; |
michael@0 | 3613 | FrameContentRange range = GetRangeForFrame(aFrame); |
michael@0 | 3614 | offsets.content = range.content; |
michael@0 | 3615 | // If there are continuations (meaning it's not one rectangle), this is the |
michael@0 | 3616 | // best this function can do |
michael@0 | 3617 | if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) { |
michael@0 | 3618 | offsets.offset = range.start; |
michael@0 | 3619 | offsets.secondaryOffset = range.end; |
michael@0 | 3620 | offsets.associateWithNext = true; |
michael@0 | 3621 | return offsets; |
michael@0 | 3622 | } |
michael@0 | 3623 | |
michael@0 | 3624 | // Figure out whether the offsets should be over, after, or before the frame |
michael@0 | 3625 | nsRect rect(nsPoint(0, 0), aFrame->GetSize()); |
michael@0 | 3626 | |
michael@0 | 3627 | bool isBlock = aFrame->GetDisplay() != NS_STYLE_DISPLAY_INLINE; |
michael@0 | 3628 | bool isRtl = (aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL); |
michael@0 | 3629 | if ((isBlock && rect.y < aPoint.y) || |
michael@0 | 3630 | (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) || |
michael@0 | 3631 | (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) { |
michael@0 | 3632 | offsets.offset = range.end; |
michael@0 | 3633 | if (rect.Contains(aPoint)) |
michael@0 | 3634 | offsets.secondaryOffset = range.start; |
michael@0 | 3635 | else |
michael@0 | 3636 | offsets.secondaryOffset = range.end; |
michael@0 | 3637 | } else { |
michael@0 | 3638 | offsets.offset = range.start; |
michael@0 | 3639 | if (rect.Contains(aPoint)) |
michael@0 | 3640 | offsets.secondaryOffset = range.end; |
michael@0 | 3641 | else |
michael@0 | 3642 | offsets.secondaryOffset = range.start; |
michael@0 | 3643 | } |
michael@0 | 3644 | offsets.associateWithNext = (offsets.offset == range.start); |
michael@0 | 3645 | return offsets; |
michael@0 | 3646 | } |
michael@0 | 3647 | |
michael@0 | 3648 | static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) { |
michael@0 | 3649 | nsIFrame* adjustedFrame = aFrame; |
michael@0 | 3650 | for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) |
michael@0 | 3651 | { |
michael@0 | 3652 | // These are the conditions that make all children not able to handle |
michael@0 | 3653 | // a cursor. |
michael@0 | 3654 | if (frame->StyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL || |
michael@0 | 3655 | frame->IsGeneratedContentFrame()) { |
michael@0 | 3656 | adjustedFrame = frame; |
michael@0 | 3657 | } |
michael@0 | 3658 | } |
michael@0 | 3659 | return adjustedFrame; |
michael@0 | 3660 | } |
michael@0 | 3661 | |
michael@0 | 3662 | nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint, |
michael@0 | 3663 | uint32_t aFlags) |
michael@0 | 3664 | { |
michael@0 | 3665 | nsIFrame *adjustedFrame; |
michael@0 | 3666 | if (aFlags & IGNORE_SELECTION_STYLE) { |
michael@0 | 3667 | adjustedFrame = this; |
michael@0 | 3668 | } |
michael@0 | 3669 | else { |
michael@0 | 3670 | // This section of code deals with special selection styles. Note that |
michael@0 | 3671 | // -moz-all exists, even though it doesn't need to be explicitly handled. |
michael@0 | 3672 | // |
michael@0 | 3673 | // The offset is forced not to end up in generated content; content offsets |
michael@0 | 3674 | // cannot represent content outside of the document's content tree. |
michael@0 | 3675 | |
michael@0 | 3676 | adjustedFrame = AdjustFrameForSelectionStyles(this); |
michael@0 | 3677 | |
michael@0 | 3678 | // -moz-user-select: all needs special handling, because clicking on it |
michael@0 | 3679 | // should lead to the whole frame being selected |
michael@0 | 3680 | if (adjustedFrame && adjustedFrame->StyleUIReset()->mUserSelect == |
michael@0 | 3681 | NS_STYLE_USER_SELECT_ALL) { |
michael@0 | 3682 | nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); |
michael@0 | 3683 | return OffsetsForSingleFrame(adjustedFrame, adjustedPoint); |
michael@0 | 3684 | } |
michael@0 | 3685 | |
michael@0 | 3686 | // For other cases, try to find a closest frame starting from the parent of |
michael@0 | 3687 | // the unselectable frame |
michael@0 | 3688 | if (adjustedFrame != this) |
michael@0 | 3689 | adjustedFrame = adjustedFrame->GetParent(); |
michael@0 | 3690 | } |
michael@0 | 3691 | |
michael@0 | 3692 | nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); |
michael@0 | 3693 | |
michael@0 | 3694 | FrameTarget closest = |
michael@0 | 3695 | GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags); |
michael@0 | 3696 | |
michael@0 | 3697 | if (closest.emptyBlock) { |
michael@0 | 3698 | ContentOffsets offsets; |
michael@0 | 3699 | NS_ASSERTION(closest.frame, |
michael@0 | 3700 | "closest.frame must not be null when it's empty"); |
michael@0 | 3701 | offsets.content = closest.frame->GetContent(); |
michael@0 | 3702 | offsets.offset = 0; |
michael@0 | 3703 | offsets.secondaryOffset = 0; |
michael@0 | 3704 | offsets.associateWithNext = true; |
michael@0 | 3705 | return offsets; |
michael@0 | 3706 | } |
michael@0 | 3707 | |
michael@0 | 3708 | // If the correct offset is at one end of a frame, use offset-based |
michael@0 | 3709 | // calculation method |
michael@0 | 3710 | if (closest.frameEdge) { |
michael@0 | 3711 | ContentOffsets offsets; |
michael@0 | 3712 | FrameContentRange range = GetRangeForFrame(closest.frame); |
michael@0 | 3713 | offsets.content = range.content; |
michael@0 | 3714 | if (closest.afterFrame) |
michael@0 | 3715 | offsets.offset = range.end; |
michael@0 | 3716 | else |
michael@0 | 3717 | offsets.offset = range.start; |
michael@0 | 3718 | offsets.secondaryOffset = offsets.offset; |
michael@0 | 3719 | offsets.associateWithNext = (offsets.offset == range.start); |
michael@0 | 3720 | return offsets; |
michael@0 | 3721 | } |
michael@0 | 3722 | |
michael@0 | 3723 | nsPoint pt; |
michael@0 | 3724 | if (closest.frame != this) { |
michael@0 | 3725 | if (closest.frame->IsSVGText()) { |
michael@0 | 3726 | pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame, |
michael@0 | 3727 | aPoint, this); |
michael@0 | 3728 | } else { |
michael@0 | 3729 | pt = aPoint - closest.frame->GetOffsetTo(this); |
michael@0 | 3730 | } |
michael@0 | 3731 | } else { |
michael@0 | 3732 | pt = aPoint; |
michael@0 | 3733 | } |
michael@0 | 3734 | return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt); |
michael@0 | 3735 | |
michael@0 | 3736 | // XXX should I add some kind of offset standardization? |
michael@0 | 3737 | // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last |
michael@0 | 3738 | // x and first z put the cursor in the same logical position in addition |
michael@0 | 3739 | // to the same visual position? |
michael@0 | 3740 | } |
michael@0 | 3741 | |
michael@0 | 3742 | nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) |
michael@0 | 3743 | { |
michael@0 | 3744 | return OffsetsForSingleFrame(this, aPoint); |
michael@0 | 3745 | } |
michael@0 | 3746 | |
michael@0 | 3747 | void |
michael@0 | 3748 | nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext) |
michael@0 | 3749 | { |
michael@0 | 3750 | if (aImage.GetType() != eStyleImageType_Image) { |
michael@0 | 3751 | return; |
michael@0 | 3752 | } |
michael@0 | 3753 | |
michael@0 | 3754 | imgIRequest *req = aImage.GetImageData(); |
michael@0 | 3755 | mozilla::css::ImageLoader* loader = |
michael@0 | 3756 | aPresContext->Document()->StyleImageLoader(); |
michael@0 | 3757 | |
michael@0 | 3758 | // If this fails there's not much we can do ... |
michael@0 | 3759 | loader->AssociateRequestToFrame(req, this); |
michael@0 | 3760 | } |
michael@0 | 3761 | |
michael@0 | 3762 | nsresult |
michael@0 | 3763 | nsFrame::GetCursor(const nsPoint& aPoint, |
michael@0 | 3764 | nsIFrame::Cursor& aCursor) |
michael@0 | 3765 | { |
michael@0 | 3766 | FillCursorInformationFromStyle(StyleUserInterface(), aCursor); |
michael@0 | 3767 | if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { |
michael@0 | 3768 | // If this is editable, I-beam cursor is better for most elements. |
michael@0 | 3769 | aCursor.mCursor = |
michael@0 | 3770 | (mContent && mContent->IsEditable()) ? NS_STYLE_CURSOR_TEXT : |
michael@0 | 3771 | NS_STYLE_CURSOR_DEFAULT; |
michael@0 | 3772 | } |
michael@0 | 3773 | |
michael@0 | 3774 | |
michael@0 | 3775 | return NS_OK; |
michael@0 | 3776 | } |
michael@0 | 3777 | |
michael@0 | 3778 | // Resize and incremental reflow |
michael@0 | 3779 | |
michael@0 | 3780 | /* virtual */ void |
michael@0 | 3781 | nsFrame::MarkIntrinsicWidthsDirty() |
michael@0 | 3782 | { |
michael@0 | 3783 | // This version is meant only for what used to be box-to-block adaptors. |
michael@0 | 3784 | // It should not be called by other derived classes. |
michael@0 | 3785 | if (IsBoxWrapped()) { |
michael@0 | 3786 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 3787 | |
michael@0 | 3788 | SizeNeedsRecalc(metrics->mPrefSize); |
michael@0 | 3789 | SizeNeedsRecalc(metrics->mMinSize); |
michael@0 | 3790 | SizeNeedsRecalc(metrics->mMaxSize); |
michael@0 | 3791 | SizeNeedsRecalc(metrics->mBlockPrefSize); |
michael@0 | 3792 | SizeNeedsRecalc(metrics->mBlockMinSize); |
michael@0 | 3793 | CoordNeedsRecalc(metrics->mFlex); |
michael@0 | 3794 | CoordNeedsRecalc(metrics->mAscent); |
michael@0 | 3795 | } |
michael@0 | 3796 | |
michael@0 | 3797 | if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) { |
michael@0 | 3798 | nsFontInflationData::MarkFontInflationDataTextDirty(this); |
michael@0 | 3799 | } |
michael@0 | 3800 | } |
michael@0 | 3801 | |
michael@0 | 3802 | /* virtual */ nscoord |
michael@0 | 3803 | nsFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 3804 | { |
michael@0 | 3805 | nscoord result = 0; |
michael@0 | 3806 | DISPLAY_MIN_WIDTH(this, result); |
michael@0 | 3807 | return result; |
michael@0 | 3808 | } |
michael@0 | 3809 | |
michael@0 | 3810 | /* virtual */ nscoord |
michael@0 | 3811 | nsFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 3812 | { |
michael@0 | 3813 | nscoord result = 0; |
michael@0 | 3814 | DISPLAY_PREF_WIDTH(this, result); |
michael@0 | 3815 | return result; |
michael@0 | 3816 | } |
michael@0 | 3817 | |
michael@0 | 3818 | /* virtual */ void |
michael@0 | 3819 | nsFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext, |
michael@0 | 3820 | nsIFrame::InlineMinWidthData *aData) |
michael@0 | 3821 | { |
michael@0 | 3822 | NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); |
michael@0 | 3823 | nsIFrame* parent = GetParent(); |
michael@0 | 3824 | bool canBreak = !CanContinueTextRun() && |
michael@0 | 3825 | parent->StyleText()->WhiteSpaceCanWrap(parent); |
michael@0 | 3826 | |
michael@0 | 3827 | if (canBreak) |
michael@0 | 3828 | aData->OptionallyBreak(aRenderingContext); |
michael@0 | 3829 | aData->trailingWhitespace = 0; |
michael@0 | 3830 | aData->skipWhitespace = false; |
michael@0 | 3831 | aData->trailingTextFrame = nullptr; |
michael@0 | 3832 | aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
michael@0 | 3833 | this, nsLayoutUtils::MIN_WIDTH); |
michael@0 | 3834 | aData->atStartOfLine = false; |
michael@0 | 3835 | if (canBreak) |
michael@0 | 3836 | aData->OptionallyBreak(aRenderingContext); |
michael@0 | 3837 | } |
michael@0 | 3838 | |
michael@0 | 3839 | /* virtual */ void |
michael@0 | 3840 | nsFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext, |
michael@0 | 3841 | nsIFrame::InlinePrefWidthData *aData) |
michael@0 | 3842 | { |
michael@0 | 3843 | aData->trailingWhitespace = 0; |
michael@0 | 3844 | aData->skipWhitespace = false; |
michael@0 | 3845 | nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
michael@0 | 3846 | this, nsLayoutUtils::PREF_WIDTH); |
michael@0 | 3847 | aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref); |
michael@0 | 3848 | } |
michael@0 | 3849 | |
michael@0 | 3850 | void |
michael@0 | 3851 | nsIFrame::InlineMinWidthData::ForceBreak(nsRenderingContext *aRenderingContext) |
michael@0 | 3852 | { |
michael@0 | 3853 | currentLine -= trailingWhitespace; |
michael@0 | 3854 | prevLines = std::max(prevLines, currentLine); |
michael@0 | 3855 | currentLine = trailingWhitespace = 0; |
michael@0 | 3856 | |
michael@0 | 3857 | for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { |
michael@0 | 3858 | nscoord float_min = floats[i].Width(); |
michael@0 | 3859 | if (float_min > prevLines) |
michael@0 | 3860 | prevLines = float_min; |
michael@0 | 3861 | } |
michael@0 | 3862 | floats.Clear(); |
michael@0 | 3863 | trailingTextFrame = nullptr; |
michael@0 | 3864 | skipWhitespace = true; |
michael@0 | 3865 | } |
michael@0 | 3866 | |
michael@0 | 3867 | void |
michael@0 | 3868 | nsIFrame::InlineMinWidthData::OptionallyBreak(nsRenderingContext *aRenderingContext, |
michael@0 | 3869 | nscoord aHyphenWidth) |
michael@0 | 3870 | { |
michael@0 | 3871 | trailingTextFrame = nullptr; |
michael@0 | 3872 | |
michael@0 | 3873 | // If we can fit more content into a smaller width by staying on this |
michael@0 | 3874 | // line (because we're still at a negative offset due to negative |
michael@0 | 3875 | // text-indent or negative margin), don't break. Otherwise, do the |
michael@0 | 3876 | // same as ForceBreak. it doesn't really matter when we accumulate |
michael@0 | 3877 | // floats. |
michael@0 | 3878 | if (currentLine + aHyphenWidth < 0 || atStartOfLine) |
michael@0 | 3879 | return; |
michael@0 | 3880 | currentLine += aHyphenWidth; |
michael@0 | 3881 | ForceBreak(aRenderingContext); |
michael@0 | 3882 | } |
michael@0 | 3883 | |
michael@0 | 3884 | void |
michael@0 | 3885 | nsIFrame::InlinePrefWidthData::ForceBreak(nsRenderingContext *aRenderingContext) |
michael@0 | 3886 | { |
michael@0 | 3887 | if (floats.Length() != 0) { |
michael@0 | 3888 | // preferred widths accumulated for floats that have already |
michael@0 | 3889 | // been cleared past |
michael@0 | 3890 | nscoord floats_done = 0, |
michael@0 | 3891 | // preferred widths accumulated for floats that have not yet |
michael@0 | 3892 | // been cleared past |
michael@0 | 3893 | floats_cur_left = 0, |
michael@0 | 3894 | floats_cur_right = 0; |
michael@0 | 3895 | |
michael@0 | 3896 | for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { |
michael@0 | 3897 | const FloatInfo& floatInfo = floats[i]; |
michael@0 | 3898 | const nsStyleDisplay *floatDisp = floatInfo.Frame()->StyleDisplay(); |
michael@0 | 3899 | if (floatDisp->mBreakType == NS_STYLE_CLEAR_LEFT || |
michael@0 | 3900 | floatDisp->mBreakType == NS_STYLE_CLEAR_RIGHT || |
michael@0 | 3901 | floatDisp->mBreakType == NS_STYLE_CLEAR_BOTH) { |
michael@0 | 3902 | nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left, |
michael@0 | 3903 | floats_cur_right); |
michael@0 | 3904 | if (floats_cur > floats_done) |
michael@0 | 3905 | floats_done = floats_cur; |
michael@0 | 3906 | if (floatDisp->mBreakType != NS_STYLE_CLEAR_RIGHT) |
michael@0 | 3907 | floats_cur_left = 0; |
michael@0 | 3908 | if (floatDisp->mBreakType != NS_STYLE_CLEAR_LEFT) |
michael@0 | 3909 | floats_cur_right = 0; |
michael@0 | 3910 | } |
michael@0 | 3911 | |
michael@0 | 3912 | nscoord &floats_cur = floatDisp->mFloats == NS_STYLE_FLOAT_LEFT |
michael@0 | 3913 | ? floats_cur_left : floats_cur_right; |
michael@0 | 3914 | nscoord floatWidth = floatInfo.Width(); |
michael@0 | 3915 | // Negative-width floats don't change the available space so they |
michael@0 | 3916 | // shouldn't change our intrinsic line width either. |
michael@0 | 3917 | floats_cur = |
michael@0 | 3918 | NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth)); |
michael@0 | 3919 | } |
michael@0 | 3920 | |
michael@0 | 3921 | nscoord floats_cur = |
michael@0 | 3922 | NSCoordSaturatingAdd(floats_cur_left, floats_cur_right); |
michael@0 | 3923 | if (floats_cur > floats_done) |
michael@0 | 3924 | floats_done = floats_cur; |
michael@0 | 3925 | |
michael@0 | 3926 | currentLine = NSCoordSaturatingAdd(currentLine, floats_done); |
michael@0 | 3927 | |
michael@0 | 3928 | floats.Clear(); |
michael@0 | 3929 | } |
michael@0 | 3930 | |
michael@0 | 3931 | currentLine = |
michael@0 | 3932 | NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX); |
michael@0 | 3933 | prevLines = std::max(prevLines, currentLine); |
michael@0 | 3934 | currentLine = trailingWhitespace = 0; |
michael@0 | 3935 | skipWhitespace = true; |
michael@0 | 3936 | } |
michael@0 | 3937 | |
michael@0 | 3938 | static void |
michael@0 | 3939 | AddCoord(const nsStyleCoord& aStyle, |
michael@0 | 3940 | nsRenderingContext* aRenderingContext, |
michael@0 | 3941 | nsIFrame* aFrame, |
michael@0 | 3942 | nscoord* aCoord, float* aPercent, |
michael@0 | 3943 | bool aClampNegativeToZero) |
michael@0 | 3944 | { |
michael@0 | 3945 | switch (aStyle.GetUnit()) { |
michael@0 | 3946 | case eStyleUnit_Coord: { |
michael@0 | 3947 | NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0, |
michael@0 | 3948 | "unexpected negative value"); |
michael@0 | 3949 | *aCoord += aStyle.GetCoordValue(); |
michael@0 | 3950 | return; |
michael@0 | 3951 | } |
michael@0 | 3952 | case eStyleUnit_Percent: { |
michael@0 | 3953 | NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f, |
michael@0 | 3954 | "unexpected negative value"); |
michael@0 | 3955 | *aPercent += aStyle.GetPercentValue(); |
michael@0 | 3956 | return; |
michael@0 | 3957 | } |
michael@0 | 3958 | case eStyleUnit_Calc: { |
michael@0 | 3959 | const nsStyleCoord::Calc *calc = aStyle.GetCalcValue(); |
michael@0 | 3960 | if (aClampNegativeToZero) { |
michael@0 | 3961 | // This is far from ideal when one is negative and one is positive. |
michael@0 | 3962 | *aCoord += std::max(calc->mLength, 0); |
michael@0 | 3963 | *aPercent += std::max(calc->mPercent, 0.0f); |
michael@0 | 3964 | } else { |
michael@0 | 3965 | *aCoord += calc->mLength; |
michael@0 | 3966 | *aPercent += calc->mPercent; |
michael@0 | 3967 | } |
michael@0 | 3968 | return; |
michael@0 | 3969 | } |
michael@0 | 3970 | default: { |
michael@0 | 3971 | return; |
michael@0 | 3972 | } |
michael@0 | 3973 | } |
michael@0 | 3974 | } |
michael@0 | 3975 | |
michael@0 | 3976 | /* virtual */ nsIFrame::IntrinsicWidthOffsetData |
michael@0 | 3977 | nsFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) |
michael@0 | 3978 | { |
michael@0 | 3979 | IntrinsicWidthOffsetData result; |
michael@0 | 3980 | |
michael@0 | 3981 | const nsStyleMargin *styleMargin = StyleMargin(); |
michael@0 | 3982 | AddCoord(styleMargin->mMargin.GetLeft(), aRenderingContext, this, |
michael@0 | 3983 | &result.hMargin, &result.hPctMargin, false); |
michael@0 | 3984 | AddCoord(styleMargin->mMargin.GetRight(), aRenderingContext, this, |
michael@0 | 3985 | &result.hMargin, &result.hPctMargin, false); |
michael@0 | 3986 | |
michael@0 | 3987 | const nsStylePadding *stylePadding = StylePadding(); |
michael@0 | 3988 | AddCoord(stylePadding->mPadding.GetLeft(), aRenderingContext, this, |
michael@0 | 3989 | &result.hPadding, &result.hPctPadding, true); |
michael@0 | 3990 | AddCoord(stylePadding->mPadding.GetRight(), aRenderingContext, this, |
michael@0 | 3991 | &result.hPadding, &result.hPctPadding, true); |
michael@0 | 3992 | |
michael@0 | 3993 | const nsStyleBorder *styleBorder = StyleBorder(); |
michael@0 | 3994 | result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_LEFT); |
michael@0 | 3995 | result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT); |
michael@0 | 3996 | |
michael@0 | 3997 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 3998 | if (IsThemed(disp)) { |
michael@0 | 3999 | nsPresContext *presContext = PresContext(); |
michael@0 | 4000 | |
michael@0 | 4001 | nsIntMargin border; |
michael@0 | 4002 | presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), |
michael@0 | 4003 | this, disp->mAppearance, |
michael@0 | 4004 | &border); |
michael@0 | 4005 | result.hBorder = presContext->DevPixelsToAppUnits(border.LeftRight()); |
michael@0 | 4006 | |
michael@0 | 4007 | nsIntMargin padding; |
michael@0 | 4008 | if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), |
michael@0 | 4009 | this, disp->mAppearance, |
michael@0 | 4010 | &padding)) { |
michael@0 | 4011 | result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight()); |
michael@0 | 4012 | result.hPctPadding = 0; |
michael@0 | 4013 | } |
michael@0 | 4014 | } |
michael@0 | 4015 | |
michael@0 | 4016 | return result; |
michael@0 | 4017 | } |
michael@0 | 4018 | |
michael@0 | 4019 | /* virtual */ IntrinsicSize |
michael@0 | 4020 | nsFrame::GetIntrinsicSize() |
michael@0 | 4021 | { |
michael@0 | 4022 | return IntrinsicSize(); // default is width/height set to eStyleUnit_None |
michael@0 | 4023 | } |
michael@0 | 4024 | |
michael@0 | 4025 | /* virtual */ nsSize |
michael@0 | 4026 | nsFrame::GetIntrinsicRatio() |
michael@0 | 4027 | { |
michael@0 | 4028 | return nsSize(0, 0); |
michael@0 | 4029 | } |
michael@0 | 4030 | |
michael@0 | 4031 | /* virtual */ nsSize |
michael@0 | 4032 | nsFrame::ComputeSize(nsRenderingContext *aRenderingContext, |
michael@0 | 4033 | nsSize aCBSize, nscoord aAvailableWidth, |
michael@0 | 4034 | nsSize aMargin, nsSize aBorder, nsSize aPadding, |
michael@0 | 4035 | uint32_t aFlags) |
michael@0 | 4036 | { |
michael@0 | 4037 | nsSize result = ComputeAutoSize(aRenderingContext, aCBSize, aAvailableWidth, |
michael@0 | 4038 | aMargin, aBorder, aPadding, |
michael@0 | 4039 | aFlags & eShrinkWrap); |
michael@0 | 4040 | nsSize boxSizingAdjust(0,0); |
michael@0 | 4041 | const nsStylePosition *stylePos = StylePosition(); |
michael@0 | 4042 | |
michael@0 | 4043 | switch (stylePos->mBoxSizing) { |
michael@0 | 4044 | case NS_STYLE_BOX_SIZING_BORDER: |
michael@0 | 4045 | boxSizingAdjust += aBorder; |
michael@0 | 4046 | // fall through |
michael@0 | 4047 | case NS_STYLE_BOX_SIZING_PADDING: |
michael@0 | 4048 | boxSizingAdjust += aPadding; |
michael@0 | 4049 | } |
michael@0 | 4050 | nscoord boxSizingToMarginEdgeWidth = |
michael@0 | 4051 | aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width; |
michael@0 | 4052 | const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth); |
michael@0 | 4053 | const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight); |
michael@0 | 4054 | |
michael@0 | 4055 | bool isFlexItem = IsFlexItem(); |
michael@0 | 4056 | bool isHorizontalFlexItem = false; |
michael@0 | 4057 | |
michael@0 | 4058 | if (isFlexItem) { |
michael@0 | 4059 | // Flex items use their "flex-basis" property in place of their main-size |
michael@0 | 4060 | // property (e.g. "width") for sizing purposes, *unless* they have |
michael@0 | 4061 | // "flex-basis:auto", in which case they use their main-size property after |
michael@0 | 4062 | // all. |
michael@0 | 4063 | uint32_t flexDirection = mParent->StylePosition()->mFlexDirection; |
michael@0 | 4064 | isHorizontalFlexItem = |
michael@0 | 4065 | flexDirection == NS_STYLE_FLEX_DIRECTION_ROW || |
michael@0 | 4066 | flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE; |
michael@0 | 4067 | |
michael@0 | 4068 | // NOTE: The logic here should match the similar chunk for determining |
michael@0 | 4069 | // widthStyleCoord and heightStyleCoord in |
michael@0 | 4070 | // nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(). |
michael@0 | 4071 | const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis); |
michael@0 | 4072 | if (flexBasis->GetUnit() != eStyleUnit_Auto) { |
michael@0 | 4073 | if (isHorizontalFlexItem) { |
michael@0 | 4074 | widthStyleCoord = flexBasis; |
michael@0 | 4075 | } else { |
michael@0 | 4076 | // One caveat for vertical flex items: We don't support enumerated |
michael@0 | 4077 | // values (e.g. "max-content") for height properties yet. So, if our |
michael@0 | 4078 | // computed flex-basis is an enumerated value, we'll just behave as if |
michael@0 | 4079 | // it were "auto", which means "use the main-size property after all" |
michael@0 | 4080 | // (which is "height", in this case). |
michael@0 | 4081 | // NOTE: Once we support intrinsic sizing keywords for "height", |
michael@0 | 4082 | // we should remove this check. |
michael@0 | 4083 | if (flexBasis->GetUnit() != eStyleUnit_Enumerated) { |
michael@0 | 4084 | heightStyleCoord = flexBasis; |
michael@0 | 4085 | } |
michael@0 | 4086 | } |
michael@0 | 4087 | } |
michael@0 | 4088 | } |
michael@0 | 4089 | |
michael@0 | 4090 | // Compute width |
michael@0 | 4091 | |
michael@0 | 4092 | if (widthStyleCoord->GetUnit() != eStyleUnit_Auto) { |
michael@0 | 4093 | result.width = |
michael@0 | 4094 | nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, |
michael@0 | 4095 | aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, |
michael@0 | 4096 | *widthStyleCoord); |
michael@0 | 4097 | } |
michael@0 | 4098 | |
michael@0 | 4099 | // Flex items ignore their min & max sizing properties in their |
michael@0 | 4100 | // flex container's main-axis. (Those properties get applied later in |
michael@0 | 4101 | // the flexbox algorithm.) |
michael@0 | 4102 | if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None && |
michael@0 | 4103 | !(isFlexItem && isHorizontalFlexItem)) { |
michael@0 | 4104 | nscoord maxWidth = |
michael@0 | 4105 | nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, |
michael@0 | 4106 | aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, |
michael@0 | 4107 | stylePos->mMaxWidth); |
michael@0 | 4108 | result.width = std::min(maxWidth, result.width); |
michael@0 | 4109 | } |
michael@0 | 4110 | |
michael@0 | 4111 | nscoord minWidth; |
michael@0 | 4112 | if (!(isFlexItem && isHorizontalFlexItem)) { |
michael@0 | 4113 | minWidth = |
michael@0 | 4114 | nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, |
michael@0 | 4115 | aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, |
michael@0 | 4116 | stylePos->mMinWidth); |
michael@0 | 4117 | } else { |
michael@0 | 4118 | minWidth = 0; |
michael@0 | 4119 | } |
michael@0 | 4120 | result.width = std::max(minWidth, result.width); |
michael@0 | 4121 | |
michael@0 | 4122 | // Compute height |
michael@0 | 4123 | // (but not if we're auto-height or if we recieved the "eUseAutoHeight" |
michael@0 | 4124 | // flag -- then, we'll just stick with the height that we already calculated |
michael@0 | 4125 | // in the initial ComputeAutoSize() call.) |
michael@0 | 4126 | if (!nsLayoutUtils::IsAutoHeight(*heightStyleCoord, aCBSize.height) && |
michael@0 | 4127 | !(aFlags & nsIFrame::eUseAutoHeight)) { |
michael@0 | 4128 | result.height = |
michael@0 | 4129 | nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
michael@0 | 4130 | boxSizingAdjust.height, |
michael@0 | 4131 | *heightStyleCoord); |
michael@0 | 4132 | } |
michael@0 | 4133 | |
michael@0 | 4134 | if (result.height != NS_UNCONSTRAINEDSIZE) { |
michael@0 | 4135 | if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) && |
michael@0 | 4136 | !(isFlexItem && !isHorizontalFlexItem)) { |
michael@0 | 4137 | nscoord maxHeight = |
michael@0 | 4138 | nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
michael@0 | 4139 | boxSizingAdjust.height, |
michael@0 | 4140 | stylePos->mMaxHeight); |
michael@0 | 4141 | result.height = std::min(maxHeight, result.height); |
michael@0 | 4142 | } |
michael@0 | 4143 | |
michael@0 | 4144 | if (!nsLayoutUtils::IsAutoHeight(stylePos->mMinHeight, aCBSize.height) && |
michael@0 | 4145 | !(isFlexItem && !isHorizontalFlexItem)) { |
michael@0 | 4146 | nscoord minHeight = |
michael@0 | 4147 | nsLayoutUtils::ComputeHeightValue(aCBSize.height, |
michael@0 | 4148 | boxSizingAdjust.height, |
michael@0 | 4149 | stylePos->mMinHeight); |
michael@0 | 4150 | result.height = std::max(minHeight, result.height); |
michael@0 | 4151 | } |
michael@0 | 4152 | } |
michael@0 | 4153 | |
michael@0 | 4154 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 4155 | if (IsThemed(disp)) { |
michael@0 | 4156 | nsIntSize widget(0, 0); |
michael@0 | 4157 | bool canOverride = true; |
michael@0 | 4158 | nsPresContext *presContext = PresContext(); |
michael@0 | 4159 | presContext->GetTheme()-> |
michael@0 | 4160 | GetMinimumWidgetSize(aRenderingContext, this, disp->mAppearance, |
michael@0 | 4161 | &widget, &canOverride); |
michael@0 | 4162 | |
michael@0 | 4163 | nsSize size; |
michael@0 | 4164 | size.width = presContext->DevPixelsToAppUnits(widget.width); |
michael@0 | 4165 | size.height = presContext->DevPixelsToAppUnits(widget.height); |
michael@0 | 4166 | |
michael@0 | 4167 | // GMWS() returns border-box; we need content-box |
michael@0 | 4168 | size.width -= aBorder.width + aPadding.width; |
michael@0 | 4169 | size.height -= aBorder.height + aPadding.height; |
michael@0 | 4170 | |
michael@0 | 4171 | if (size.height > result.height || !canOverride) |
michael@0 | 4172 | result.height = size.height; |
michael@0 | 4173 | if (size.width > result.width || !canOverride) |
michael@0 | 4174 | result.width = size.width; |
michael@0 | 4175 | } |
michael@0 | 4176 | |
michael@0 | 4177 | result.width = std::max(0, result.width); |
michael@0 | 4178 | result.height = std::max(0, result.height); |
michael@0 | 4179 | |
michael@0 | 4180 | return result; |
michael@0 | 4181 | } |
michael@0 | 4182 | |
michael@0 | 4183 | nsRect |
michael@0 | 4184 | nsIFrame::ComputeTightBounds(gfxContext* aContext) const |
michael@0 | 4185 | { |
michael@0 | 4186 | return GetVisualOverflowRect(); |
michael@0 | 4187 | } |
michael@0 | 4188 | |
michael@0 | 4189 | nsRect |
michael@0 | 4190 | nsFrame::ComputeSimpleTightBounds(gfxContext* aContext) const |
michael@0 | 4191 | { |
michael@0 | 4192 | if (StyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE || |
michael@0 | 4193 | StyleBorder()->HasBorder() || !StyleBackground()->IsTransparent() || |
michael@0 | 4194 | StyleDisplay()->mAppearance) { |
michael@0 | 4195 | // Not necessarily tight, due to clipping, negative |
michael@0 | 4196 | // outline-offset, and lots of other issues, but that's OK |
michael@0 | 4197 | return GetVisualOverflowRect(); |
michael@0 | 4198 | } |
michael@0 | 4199 | |
michael@0 | 4200 | nsRect r(0, 0, 0, 0); |
michael@0 | 4201 | ChildListIterator lists(this); |
michael@0 | 4202 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 4203 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 4204 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 4205 | nsIFrame* child = childFrames.get(); |
michael@0 | 4206 | r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition()); |
michael@0 | 4207 | } |
michael@0 | 4208 | } |
michael@0 | 4209 | return r; |
michael@0 | 4210 | } |
michael@0 | 4211 | |
michael@0 | 4212 | /* virtual */ nsresult |
michael@0 | 4213 | nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext, |
michael@0 | 4214 | nscoord* aX, |
michael@0 | 4215 | nscoord* aXMost) |
michael@0 | 4216 | { |
michael@0 | 4217 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 4218 | } |
michael@0 | 4219 | |
michael@0 | 4220 | /* virtual */ nsSize |
michael@0 | 4221 | nsFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
michael@0 | 4222 | nsSize aCBSize, nscoord aAvailableWidth, |
michael@0 | 4223 | nsSize aMargin, nsSize aBorder, nsSize aPadding, |
michael@0 | 4224 | bool aShrinkWrap) |
michael@0 | 4225 | { |
michael@0 | 4226 | // Use basic shrink-wrapping as a default implementation. |
michael@0 | 4227 | nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE); |
michael@0 | 4228 | |
michael@0 | 4229 | // don't bother setting it if the result won't be used |
michael@0 | 4230 | if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { |
michael@0 | 4231 | nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width - |
michael@0 | 4232 | aPadding.width; |
michael@0 | 4233 | result.width = ShrinkWidthToFit(aRenderingContext, availBased); |
michael@0 | 4234 | } |
michael@0 | 4235 | return result; |
michael@0 | 4236 | } |
michael@0 | 4237 | |
michael@0 | 4238 | nscoord |
michael@0 | 4239 | nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext, |
michael@0 | 4240 | nscoord aWidthInCB) |
michael@0 | 4241 | { |
michael@0 | 4242 | // If we're a container for font size inflation, then shrink |
michael@0 | 4243 | // wrapping inside of us should not apply font size inflation. |
michael@0 | 4244 | AutoMaybeDisableFontInflation an(this); |
michael@0 | 4245 | |
michael@0 | 4246 | nscoord result; |
michael@0 | 4247 | nscoord minWidth = GetMinWidth(aRenderingContext); |
michael@0 | 4248 | if (minWidth > aWidthInCB) { |
michael@0 | 4249 | result = minWidth; |
michael@0 | 4250 | } else { |
michael@0 | 4251 | nscoord prefWidth = GetPrefWidth(aRenderingContext); |
michael@0 | 4252 | if (prefWidth > aWidthInCB) { |
michael@0 | 4253 | result = aWidthInCB; |
michael@0 | 4254 | } else { |
michael@0 | 4255 | result = prefWidth; |
michael@0 | 4256 | } |
michael@0 | 4257 | } |
michael@0 | 4258 | return result; |
michael@0 | 4259 | } |
michael@0 | 4260 | |
michael@0 | 4261 | nsresult |
michael@0 | 4262 | nsFrame::WillReflow(nsPresContext* aPresContext) |
michael@0 | 4263 | { |
michael@0 | 4264 | #ifdef DEBUG_dbaron_off |
michael@0 | 4265 | // bug 81268 |
michael@0 | 4266 | NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), |
michael@0 | 4267 | "nsFrame::WillReflow: frame is already in reflow"); |
michael@0 | 4268 | #endif |
michael@0 | 4269 | |
michael@0 | 4270 | NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, |
michael@0 | 4271 | ("WillReflow: oldState=%x", mState)); |
michael@0 | 4272 | mState |= NS_FRAME_IN_REFLOW; |
michael@0 | 4273 | return NS_OK; |
michael@0 | 4274 | } |
michael@0 | 4275 | |
michael@0 | 4276 | nsresult |
michael@0 | 4277 | nsFrame::DidReflow(nsPresContext* aPresContext, |
michael@0 | 4278 | const nsHTMLReflowState* aReflowState, |
michael@0 | 4279 | nsDidReflowStatus aStatus) |
michael@0 | 4280 | { |
michael@0 | 4281 | NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, |
michael@0 | 4282 | ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus))); |
michael@0 | 4283 | |
michael@0 | 4284 | nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW); |
michael@0 | 4285 | |
michael@0 | 4286 | if (nsDidReflowStatus::FINISHED == aStatus) { |
michael@0 | 4287 | mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
michael@0 | 4288 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 4289 | } |
michael@0 | 4290 | |
michael@0 | 4291 | // Notify the percent height observer if there is a percent height. |
michael@0 | 4292 | // The observer may be able to initiate another reflow with a computed |
michael@0 | 4293 | // height. This happens in the case where a table cell has no computed |
michael@0 | 4294 | // height but can fabricate one when the cell height is known. |
michael@0 | 4295 | if (aReflowState && aReflowState->mPercentHeightObserver && |
michael@0 | 4296 | !GetPrevInFlow()) { |
michael@0 | 4297 | const nsStyleCoord &height = aReflowState->mStylePosition->mHeight; |
michael@0 | 4298 | if (height.HasPercent()) { |
michael@0 | 4299 | aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState); |
michael@0 | 4300 | } |
michael@0 | 4301 | } |
michael@0 | 4302 | |
michael@0 | 4303 | return NS_OK; |
michael@0 | 4304 | } |
michael@0 | 4305 | |
michael@0 | 4306 | void |
michael@0 | 4307 | nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, |
michael@0 | 4308 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 4309 | const nsHTMLReflowState& aReflowState, |
michael@0 | 4310 | nsReflowStatus& aStatus, |
michael@0 | 4311 | bool aConstrainHeight) |
michael@0 | 4312 | { |
michael@0 | 4313 | ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, aConstrainHeight); |
michael@0 | 4314 | |
michael@0 | 4315 | FinishAndStoreOverflow(&aDesiredSize); |
michael@0 | 4316 | } |
michael@0 | 4317 | |
michael@0 | 4318 | void |
michael@0 | 4319 | nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, |
michael@0 | 4320 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 4321 | const nsHTMLReflowState& aReflowState, |
michael@0 | 4322 | nsReflowStatus& aStatus, |
michael@0 | 4323 | bool aConstrainHeight) |
michael@0 | 4324 | { |
michael@0 | 4325 | if (HasAbsolutelyPositionedChildren()) { |
michael@0 | 4326 | nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock(); |
michael@0 | 4327 | |
michael@0 | 4328 | // Let the absolutely positioned container reflow any absolutely positioned |
michael@0 | 4329 | // child frames that need to be reflowed |
michael@0 | 4330 | |
michael@0 | 4331 | // The containing block for the abs pos kids is formed by our padding edge. |
michael@0 | 4332 | nsMargin computedBorder = |
michael@0 | 4333 | aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding(); |
michael@0 | 4334 | nscoord containingBlockWidth = |
michael@0 | 4335 | aDesiredSize.Width() - computedBorder.LeftRight(); |
michael@0 | 4336 | nscoord containingBlockHeight = |
michael@0 | 4337 | aDesiredSize.Height() - computedBorder.TopBottom(); |
michael@0 | 4338 | |
michael@0 | 4339 | nsContainerFrame* container = do_QueryFrame(this); |
michael@0 | 4340 | NS_ASSERTION(container, "Abs-pos children only supported on container frames for now"); |
michael@0 | 4341 | |
michael@0 | 4342 | nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight); |
michael@0 | 4343 | absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus, |
michael@0 | 4344 | containingBlock, |
michael@0 | 4345 | aConstrainHeight, true, true, // XXX could be optimized |
michael@0 | 4346 | &aDesiredSize.mOverflowAreas); |
michael@0 | 4347 | } |
michael@0 | 4348 | } |
michael@0 | 4349 | |
michael@0 | 4350 | /* virtual */ bool |
michael@0 | 4351 | nsFrame::CanContinueTextRun() const |
michael@0 | 4352 | { |
michael@0 | 4353 | // By default, a frame will *not* allow a text run to be continued |
michael@0 | 4354 | // through it. |
michael@0 | 4355 | return false; |
michael@0 | 4356 | } |
michael@0 | 4357 | |
michael@0 | 4358 | nsresult |
michael@0 | 4359 | nsFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 4360 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 4361 | const nsHTMLReflowState& aReflowState, |
michael@0 | 4362 | nsReflowStatus& aStatus) |
michael@0 | 4363 | { |
michael@0 | 4364 | DO_GLOBAL_REFLOW_COUNT("nsFrame"); |
michael@0 | 4365 | aDesiredSize.Width() = 0; |
michael@0 | 4366 | aDesiredSize.Height() = 0; |
michael@0 | 4367 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 4368 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 4369 | return NS_OK; |
michael@0 | 4370 | } |
michael@0 | 4371 | |
michael@0 | 4372 | nsresult |
michael@0 | 4373 | nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) |
michael@0 | 4374 | { |
michael@0 | 4375 | NS_NOTREACHED("should only be called for text frames"); |
michael@0 | 4376 | return NS_OK; |
michael@0 | 4377 | } |
michael@0 | 4378 | |
michael@0 | 4379 | nsresult |
michael@0 | 4380 | nsFrame::AttributeChanged(int32_t aNameSpaceID, |
michael@0 | 4381 | nsIAtom* aAttribute, |
michael@0 | 4382 | int32_t aModType) |
michael@0 | 4383 | { |
michael@0 | 4384 | return NS_OK; |
michael@0 | 4385 | } |
michael@0 | 4386 | |
michael@0 | 4387 | // Flow member functions |
michael@0 | 4388 | |
michael@0 | 4389 | nsSplittableType |
michael@0 | 4390 | nsFrame::GetSplittableType() const |
michael@0 | 4391 | { |
michael@0 | 4392 | return NS_FRAME_NOT_SPLITTABLE; |
michael@0 | 4393 | } |
michael@0 | 4394 | |
michael@0 | 4395 | nsIFrame* nsFrame::GetPrevContinuation() const |
michael@0 | 4396 | { |
michael@0 | 4397 | return nullptr; |
michael@0 | 4398 | } |
michael@0 | 4399 | |
michael@0 | 4400 | void |
michael@0 | 4401 | nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) |
michael@0 | 4402 | { |
michael@0 | 4403 | MOZ_ASSERT(false, "not splittable"); |
michael@0 | 4404 | } |
michael@0 | 4405 | |
michael@0 | 4406 | nsIFrame* nsFrame::GetNextContinuation() const |
michael@0 | 4407 | { |
michael@0 | 4408 | return nullptr; |
michael@0 | 4409 | } |
michael@0 | 4410 | |
michael@0 | 4411 | void |
michael@0 | 4412 | nsFrame::SetNextContinuation(nsIFrame*) |
michael@0 | 4413 | { |
michael@0 | 4414 | MOZ_ASSERT(false, "not splittable"); |
michael@0 | 4415 | } |
michael@0 | 4416 | |
michael@0 | 4417 | nsIFrame* nsFrame::GetPrevInFlowVirtual() const |
michael@0 | 4418 | { |
michael@0 | 4419 | return nullptr; |
michael@0 | 4420 | } |
michael@0 | 4421 | |
michael@0 | 4422 | void |
michael@0 | 4423 | nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) |
michael@0 | 4424 | { |
michael@0 | 4425 | MOZ_ASSERT(false, "not splittable"); |
michael@0 | 4426 | } |
michael@0 | 4427 | |
michael@0 | 4428 | nsIFrame* nsFrame::GetNextInFlowVirtual() const |
michael@0 | 4429 | { |
michael@0 | 4430 | return nullptr; |
michael@0 | 4431 | } |
michael@0 | 4432 | |
michael@0 | 4433 | void |
michael@0 | 4434 | nsFrame::SetNextInFlow(nsIFrame*) |
michael@0 | 4435 | { |
michael@0 | 4436 | MOZ_ASSERT(false, "not splittable"); |
michael@0 | 4437 | } |
michael@0 | 4438 | |
michael@0 | 4439 | nsIFrame* nsIFrame::GetTailContinuation() |
michael@0 | 4440 | { |
michael@0 | 4441 | nsIFrame* frame = this; |
michael@0 | 4442 | while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { |
michael@0 | 4443 | frame = frame->GetPrevContinuation(); |
michael@0 | 4444 | NS_ASSERTION(frame, "first continuation can't be overflow container"); |
michael@0 | 4445 | } |
michael@0 | 4446 | for (nsIFrame* next = frame->GetNextContinuation(); |
michael@0 | 4447 | next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER); |
michael@0 | 4448 | next = frame->GetNextContinuation()) { |
michael@0 | 4449 | frame = next; |
michael@0 | 4450 | } |
michael@0 | 4451 | NS_POSTCONDITION(frame, "illegal state in continuation chain."); |
michael@0 | 4452 | return frame; |
michael@0 | 4453 | } |
michael@0 | 4454 | |
michael@0 | 4455 | NS_DECLARE_FRAME_PROPERTY(ViewProperty, nullptr) |
michael@0 | 4456 | |
michael@0 | 4457 | // Associated view object |
michael@0 | 4458 | nsView* |
michael@0 | 4459 | nsIFrame::GetView() const |
michael@0 | 4460 | { |
michael@0 | 4461 | // Check the frame state bit and see if the frame has a view |
michael@0 | 4462 | if (!(GetStateBits() & NS_FRAME_HAS_VIEW)) |
michael@0 | 4463 | return nullptr; |
michael@0 | 4464 | |
michael@0 | 4465 | // Check for a property on the frame |
michael@0 | 4466 | void* value = Properties().Get(ViewProperty()); |
michael@0 | 4467 | NS_ASSERTION(value, "frame state bit was set but frame has no view"); |
michael@0 | 4468 | return static_cast<nsView*>(value); |
michael@0 | 4469 | } |
michael@0 | 4470 | |
michael@0 | 4471 | /* virtual */ nsView* |
michael@0 | 4472 | nsIFrame::GetViewExternal() const |
michael@0 | 4473 | { |
michael@0 | 4474 | return GetView(); |
michael@0 | 4475 | } |
michael@0 | 4476 | |
michael@0 | 4477 | nsresult |
michael@0 | 4478 | nsIFrame::SetView(nsView* aView) |
michael@0 | 4479 | { |
michael@0 | 4480 | if (aView) { |
michael@0 | 4481 | aView->SetFrame(this); |
michael@0 | 4482 | |
michael@0 | 4483 | #ifdef DEBUG |
michael@0 | 4484 | nsIAtom* frameType = GetType(); |
michael@0 | 4485 | NS_ASSERTION(frameType == nsGkAtoms::scrollFrame || |
michael@0 | 4486 | frameType == nsGkAtoms::subDocumentFrame || |
michael@0 | 4487 | frameType == nsGkAtoms::listControlFrame || |
michael@0 | 4488 | frameType == nsGkAtoms::objectFrame || |
michael@0 | 4489 | frameType == nsGkAtoms::viewportFrame || |
michael@0 | 4490 | frameType == nsGkAtoms::menuPopupFrame, |
michael@0 | 4491 | "Only specific frame types can have an nsView"); |
michael@0 | 4492 | #endif |
michael@0 | 4493 | |
michael@0 | 4494 | // Set a property on the frame |
michael@0 | 4495 | Properties().Set(ViewProperty(), aView); |
michael@0 | 4496 | |
michael@0 | 4497 | // Set the frame state bit that says the frame has a view |
michael@0 | 4498 | AddStateBits(NS_FRAME_HAS_VIEW); |
michael@0 | 4499 | |
michael@0 | 4500 | // Let all of the ancestors know they have a descendant with a view. |
michael@0 | 4501 | for (nsIFrame* f = GetParent(); |
michael@0 | 4502 | f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW); |
michael@0 | 4503 | f = f->GetParent()) |
michael@0 | 4504 | f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); |
michael@0 | 4505 | } |
michael@0 | 4506 | |
michael@0 | 4507 | return NS_OK; |
michael@0 | 4508 | } |
michael@0 | 4509 | |
michael@0 | 4510 | nsIFrame* nsIFrame::GetAncestorWithViewExternal() const |
michael@0 | 4511 | { |
michael@0 | 4512 | return GetAncestorWithView(); |
michael@0 | 4513 | } |
michael@0 | 4514 | |
michael@0 | 4515 | // Find the first geometric parent that has a view |
michael@0 | 4516 | nsIFrame* nsIFrame::GetAncestorWithView() const |
michael@0 | 4517 | { |
michael@0 | 4518 | for (nsIFrame* f = mParent; nullptr != f; f = f->GetParent()) { |
michael@0 | 4519 | if (f->HasView()) { |
michael@0 | 4520 | return f; |
michael@0 | 4521 | } |
michael@0 | 4522 | } |
michael@0 | 4523 | return nullptr; |
michael@0 | 4524 | } |
michael@0 | 4525 | |
michael@0 | 4526 | // virtual |
michael@0 | 4527 | nsPoint nsIFrame::GetOffsetToExternal(const nsIFrame* aOther) const |
michael@0 | 4528 | { |
michael@0 | 4529 | return GetOffsetTo(aOther); |
michael@0 | 4530 | } |
michael@0 | 4531 | |
michael@0 | 4532 | nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const |
michael@0 | 4533 | { |
michael@0 | 4534 | NS_PRECONDITION(aOther, |
michael@0 | 4535 | "Must have frame for destination coordinate system!"); |
michael@0 | 4536 | |
michael@0 | 4537 | NS_ASSERTION(PresContext() == aOther->PresContext(), |
michael@0 | 4538 | "GetOffsetTo called on frames in different documents"); |
michael@0 | 4539 | |
michael@0 | 4540 | nsPoint offset(0, 0); |
michael@0 | 4541 | const nsIFrame* f; |
michael@0 | 4542 | for (f = this; f != aOther && f; f = f->GetParent()) { |
michael@0 | 4543 | offset += f->GetPosition(); |
michael@0 | 4544 | } |
michael@0 | 4545 | |
michael@0 | 4546 | if (f != aOther) { |
michael@0 | 4547 | // Looks like aOther wasn't an ancestor of |this|. So now we have |
michael@0 | 4548 | // the root-frame-relative position of |this| in |offset|. Convert back |
michael@0 | 4549 | // to the coordinates of aOther |
michael@0 | 4550 | while (aOther) { |
michael@0 | 4551 | offset -= aOther->GetPosition(); |
michael@0 | 4552 | aOther = aOther->GetParent(); |
michael@0 | 4553 | } |
michael@0 | 4554 | } |
michael@0 | 4555 | |
michael@0 | 4556 | return offset; |
michael@0 | 4557 | } |
michael@0 | 4558 | |
michael@0 | 4559 | nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const |
michael@0 | 4560 | { |
michael@0 | 4561 | return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel()); |
michael@0 | 4562 | } |
michael@0 | 4563 | |
michael@0 | 4564 | nsPoint |
michael@0 | 4565 | nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const |
michael@0 | 4566 | { |
michael@0 | 4567 | NS_PRECONDITION(aOther, |
michael@0 | 4568 | "Must have frame for destination coordinate system!"); |
michael@0 | 4569 | NS_ASSERTION(PresContext()->GetRootPresContext() == |
michael@0 | 4570 | aOther->PresContext()->GetRootPresContext(), |
michael@0 | 4571 | "trying to get the offset between frames in different document " |
michael@0 | 4572 | "hierarchies?"); |
michael@0 | 4573 | if (PresContext()->GetRootPresContext() != |
michael@0 | 4574 | aOther->PresContext()->GetRootPresContext()) { |
michael@0 | 4575 | // crash right away, we are almost certainly going to crash anyway. |
michael@0 | 4576 | NS_RUNTIMEABORT("trying to get the offset between frames in different " |
michael@0 | 4577 | "document hierarchies?"); |
michael@0 | 4578 | } |
michael@0 | 4579 | |
michael@0 | 4580 | const nsIFrame* root = nullptr; |
michael@0 | 4581 | // offset will hold the final offset |
michael@0 | 4582 | // docOffset holds the currently accumulated offset at the current APD, it |
michael@0 | 4583 | // will be converted and added to offset when the current APD changes. |
michael@0 | 4584 | nsPoint offset(0, 0), docOffset(0, 0); |
michael@0 | 4585 | const nsIFrame* f = this; |
michael@0 | 4586 | int32_t currAPD = PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4587 | while (f && f != aOther) { |
michael@0 | 4588 | docOffset += f->GetPosition(); |
michael@0 | 4589 | nsIFrame* parent = f->GetParent(); |
michael@0 | 4590 | if (parent) { |
michael@0 | 4591 | f = parent; |
michael@0 | 4592 | } else { |
michael@0 | 4593 | nsPoint newOffset(0, 0); |
michael@0 | 4594 | root = f; |
michael@0 | 4595 | f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset); |
michael@0 | 4596 | int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0; |
michael@0 | 4597 | if (!f || newAPD != currAPD) { |
michael@0 | 4598 | // Convert docOffset to the right APD and add it to offset. |
michael@0 | 4599 | offset += docOffset.ConvertAppUnits(currAPD, aAPD); |
michael@0 | 4600 | docOffset.x = docOffset.y = 0; |
michael@0 | 4601 | } |
michael@0 | 4602 | currAPD = newAPD; |
michael@0 | 4603 | docOffset += newOffset; |
michael@0 | 4604 | } |
michael@0 | 4605 | } |
michael@0 | 4606 | if (f == aOther) { |
michael@0 | 4607 | offset += docOffset.ConvertAppUnits(currAPD, aAPD); |
michael@0 | 4608 | } else { |
michael@0 | 4609 | // Looks like aOther wasn't an ancestor of |this|. So now we have |
michael@0 | 4610 | // the root-document-relative position of |this| in |offset|. Subtract the |
michael@0 | 4611 | // root-document-relative position of |aOther| from |offset|. |
michael@0 | 4612 | // This call won't try to recurse again because root is an ancestor of |
michael@0 | 4613 | // aOther. |
michael@0 | 4614 | nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD); |
michael@0 | 4615 | offset -= negOffset; |
michael@0 | 4616 | } |
michael@0 | 4617 | |
michael@0 | 4618 | return offset; |
michael@0 | 4619 | } |
michael@0 | 4620 | |
michael@0 | 4621 | // virtual |
michael@0 | 4622 | nsIntRect nsIFrame::GetScreenRectExternal() const |
michael@0 | 4623 | { |
michael@0 | 4624 | return GetScreenRect(); |
michael@0 | 4625 | } |
michael@0 | 4626 | |
michael@0 | 4627 | nsIntRect nsIFrame::GetScreenRect() const |
michael@0 | 4628 | { |
michael@0 | 4629 | return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel()); |
michael@0 | 4630 | } |
michael@0 | 4631 | |
michael@0 | 4632 | // virtual |
michael@0 | 4633 | nsRect nsIFrame::GetScreenRectInAppUnitsExternal() const |
michael@0 | 4634 | { |
michael@0 | 4635 | return GetScreenRectInAppUnits(); |
michael@0 | 4636 | } |
michael@0 | 4637 | |
michael@0 | 4638 | nsRect nsIFrame::GetScreenRectInAppUnits() const |
michael@0 | 4639 | { |
michael@0 | 4640 | nsPresContext* presContext = PresContext(); |
michael@0 | 4641 | nsIFrame* rootFrame = |
michael@0 | 4642 | presContext->PresShell()->FrameManager()->GetRootFrame(); |
michael@0 | 4643 | nsPoint rootScreenPos(0, 0); |
michael@0 | 4644 | nsPoint rootFrameOffsetInParent(0, 0); |
michael@0 | 4645 | nsIFrame* rootFrameParent = |
michael@0 | 4646 | nsLayoutUtils::GetCrossDocParentFrame(rootFrame, &rootFrameOffsetInParent); |
michael@0 | 4647 | if (rootFrameParent) { |
michael@0 | 4648 | nsRect parentScreenRectAppUnits = rootFrameParent->GetScreenRectInAppUnits(); |
michael@0 | 4649 | nsPresContext* parentPresContext = rootFrameParent->PresContext(); |
michael@0 | 4650 | double parentScale = double(presContext->AppUnitsPerDevPixel())/ |
michael@0 | 4651 | parentPresContext->AppUnitsPerDevPixel(); |
michael@0 | 4652 | nsPoint rootPt = parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent; |
michael@0 | 4653 | rootScreenPos.x = NS_round(parentScale*rootPt.x); |
michael@0 | 4654 | rootScreenPos.y = NS_round(parentScale*rootPt.y); |
michael@0 | 4655 | } else { |
michael@0 | 4656 | nsCOMPtr<nsIWidget> rootWidget; |
michael@0 | 4657 | presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget)); |
michael@0 | 4658 | if (rootWidget) { |
michael@0 | 4659 | nsIntPoint rootDevPx = rootWidget->WidgetToScreenOffset(); |
michael@0 | 4660 | rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x); |
michael@0 | 4661 | rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y); |
michael@0 | 4662 | } |
michael@0 | 4663 | } |
michael@0 | 4664 | |
michael@0 | 4665 | return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize()); |
michael@0 | 4666 | } |
michael@0 | 4667 | |
michael@0 | 4668 | // Returns the offset from this frame to the closest geometric parent that |
michael@0 | 4669 | // has a view. Also returns the containing view or null in case of error |
michael@0 | 4670 | void |
michael@0 | 4671 | nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const |
michael@0 | 4672 | { |
michael@0 | 4673 | NS_PRECONDITION(nullptr != aView, "null OUT parameter pointer"); |
michael@0 | 4674 | nsIFrame* frame = const_cast<nsIFrame*>(this); |
michael@0 | 4675 | |
michael@0 | 4676 | *aView = nullptr; |
michael@0 | 4677 | aOffset.MoveTo(0, 0); |
michael@0 | 4678 | do { |
michael@0 | 4679 | aOffset += frame->GetPosition(); |
michael@0 | 4680 | frame = frame->GetParent(); |
michael@0 | 4681 | } while (frame && !frame->HasView()); |
michael@0 | 4682 | |
michael@0 | 4683 | if (frame) { |
michael@0 | 4684 | *aView = frame->GetView(); |
michael@0 | 4685 | } |
michael@0 | 4686 | } |
michael@0 | 4687 | |
michael@0 | 4688 | nsIWidget* |
michael@0 | 4689 | nsIFrame::GetNearestWidget() const |
michael@0 | 4690 | { |
michael@0 | 4691 | return GetClosestView()->GetNearestWidget(nullptr); |
michael@0 | 4692 | } |
michael@0 | 4693 | |
michael@0 | 4694 | nsIWidget* |
michael@0 | 4695 | nsIFrame::GetNearestWidget(nsPoint& aOffset) const |
michael@0 | 4696 | { |
michael@0 | 4697 | nsPoint offsetToView; |
michael@0 | 4698 | nsPoint offsetToWidget; |
michael@0 | 4699 | nsIWidget* widget = |
michael@0 | 4700 | GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget); |
michael@0 | 4701 | aOffset = offsetToView + offsetToWidget; |
michael@0 | 4702 | return widget; |
michael@0 | 4703 | } |
michael@0 | 4704 | |
michael@0 | 4705 | nsIAtom* |
michael@0 | 4706 | nsFrame::GetType() const |
michael@0 | 4707 | { |
michael@0 | 4708 | return nullptr; |
michael@0 | 4709 | } |
michael@0 | 4710 | |
michael@0 | 4711 | bool |
michael@0 | 4712 | nsIFrame::IsLeaf() const |
michael@0 | 4713 | { |
michael@0 | 4714 | return true; |
michael@0 | 4715 | } |
michael@0 | 4716 | |
michael@0 | 4717 | gfx3DMatrix |
michael@0 | 4718 | nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor, |
michael@0 | 4719 | nsIFrame** aOutAncestor) |
michael@0 | 4720 | { |
michael@0 | 4721 | NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); |
michael@0 | 4722 | |
michael@0 | 4723 | /* If we're transformed, we want to hand back the combination |
michael@0 | 4724 | * transform/translate matrix that will apply our current transform, then |
michael@0 | 4725 | * shift us to our parent. |
michael@0 | 4726 | */ |
michael@0 | 4727 | if (IsTransformed()) { |
michael@0 | 4728 | /* Compute the delta to the parent, which we need because we are converting |
michael@0 | 4729 | * coordinates to our parent. |
michael@0 | 4730 | */ |
michael@0 | 4731 | NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), |
michael@0 | 4732 | "Cannot transform the viewport frame!"); |
michael@0 | 4733 | int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4734 | |
michael@0 | 4735 | gfx3DMatrix result = |
michael@0 | 4736 | nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), scaleFactor, nullptr, aOutAncestor); |
michael@0 | 4737 | // XXXjwatt: seems like this will double count offsets in the face of preserve-3d: |
michael@0 | 4738 | nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); |
michael@0 | 4739 | /* Combine the raw transform with a translation to our parent. */ |
michael@0 | 4740 | result *= gfx3DMatrix::Translation |
michael@0 | 4741 | (NSAppUnitsToFloatPixels(delta.x, scaleFactor), |
michael@0 | 4742 | NSAppUnitsToFloatPixels(delta.y, scaleFactor), |
michael@0 | 4743 | 0.0f); |
michael@0 | 4744 | return result; |
michael@0 | 4745 | } |
michael@0 | 4746 | |
michael@0 | 4747 | if (nsLayoutUtils::IsPopup(this) && |
michael@0 | 4748 | GetType() == nsGkAtoms::listControlFrame) { |
michael@0 | 4749 | nsPresContext* presContext = PresContext(); |
michael@0 | 4750 | nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame(); |
michael@0 | 4751 | |
michael@0 | 4752 | // Compute a matrix that transforms from the popup widget to the toplevel |
michael@0 | 4753 | // widget. We use the widgets because they're the simplest and most |
michael@0 | 4754 | // accurate approach --- this should work no matter how the widget position |
michael@0 | 4755 | // was chosen. |
michael@0 | 4756 | nsIWidget* widget = GetView()->GetWidget(); |
michael@0 | 4757 | nsPresContext* rootPresContext = PresContext()->GetRootPresContext(); |
michael@0 | 4758 | // Maybe the widget hasn't been created yet? Popups without widgets are |
michael@0 | 4759 | // treated as regular frames. That should work since they'll be rendered |
michael@0 | 4760 | // as part of the page if they're rendered at all. |
michael@0 | 4761 | if (widget && rootPresContext) { |
michael@0 | 4762 | nsIWidget* toplevel = rootPresContext->GetNearestWidget(); |
michael@0 | 4763 | if (toplevel) { |
michael@0 | 4764 | nsIntRect screenBounds; |
michael@0 | 4765 | widget->GetClientBounds(screenBounds); |
michael@0 | 4766 | nsIntRect toplevelScreenBounds; |
michael@0 | 4767 | toplevel->GetClientBounds(toplevelScreenBounds); |
michael@0 | 4768 | nsIntPoint translation = screenBounds.TopLeft() - toplevelScreenBounds.TopLeft(); |
michael@0 | 4769 | |
michael@0 | 4770 | gfx3DMatrix transformToTop; |
michael@0 | 4771 | transformToTop._41 = translation.x; |
michael@0 | 4772 | transformToTop._42 = translation.y; |
michael@0 | 4773 | |
michael@0 | 4774 | *aOutAncestor = docRootFrame; |
michael@0 | 4775 | gfx3DMatrix docRootTransformToTop = |
michael@0 | 4776 | nsLayoutUtils::GetTransformToAncestor(docRootFrame, nullptr); |
michael@0 | 4777 | if (docRootTransformToTop.IsSingular()) { |
michael@0 | 4778 | NS_WARNING("Containing document is invisible, we can't compute a valid transform"); |
michael@0 | 4779 | } else { |
michael@0 | 4780 | gfx3DMatrix topToDocRootTransform = docRootTransformToTop.Inverse(); |
michael@0 | 4781 | return transformToTop*topToDocRootTransform; |
michael@0 | 4782 | } |
michael@0 | 4783 | } |
michael@0 | 4784 | } |
michael@0 | 4785 | } |
michael@0 | 4786 | |
michael@0 | 4787 | *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this); |
michael@0 | 4788 | |
michael@0 | 4789 | /* Otherwise, we're not transformed. In that case, we'll walk up the frame |
michael@0 | 4790 | * tree until we either hit the root frame or something that may be |
michael@0 | 4791 | * transformed. We'll then change coordinates into that frame, since we're |
michael@0 | 4792 | * guaranteed that nothing in-between can be transformed. First, however, |
michael@0 | 4793 | * we have to check to see if we have a parent. If not, we'll set the |
michael@0 | 4794 | * outparam to null (indicating that there's nothing left) and will hand back |
michael@0 | 4795 | * the identity matrix. |
michael@0 | 4796 | */ |
michael@0 | 4797 | if (!*aOutAncestor) |
michael@0 | 4798 | return gfx3DMatrix(); |
michael@0 | 4799 | |
michael@0 | 4800 | /* Keep iterating while the frame can't possibly be transformed. */ |
michael@0 | 4801 | while (!(*aOutAncestor)->IsTransformed() && |
michael@0 | 4802 | !nsLayoutUtils::IsPopup(*aOutAncestor) && |
michael@0 | 4803 | *aOutAncestor != aStopAtAncestor) { |
michael@0 | 4804 | /* If no parent, stop iterating. Otherwise, update the ancestor. */ |
michael@0 | 4805 | nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor); |
michael@0 | 4806 | if (!parent) |
michael@0 | 4807 | break; |
michael@0 | 4808 | |
michael@0 | 4809 | *aOutAncestor = parent; |
michael@0 | 4810 | } |
michael@0 | 4811 | |
michael@0 | 4812 | NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?"); |
michael@0 | 4813 | |
michael@0 | 4814 | /* Translate from this frame to our ancestor, if it exists. That's the |
michael@0 | 4815 | * entire transform, so we're done. |
michael@0 | 4816 | */ |
michael@0 | 4817 | nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); |
michael@0 | 4818 | int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4819 | return gfx3DMatrix().Translation |
michael@0 | 4820 | (NSAppUnitsToFloatPixels(delta.x, scaleFactor), |
michael@0 | 4821 | NSAppUnitsToFloatPixels(delta.y, scaleFactor), |
michael@0 | 4822 | 0.0f); |
michael@0 | 4823 | } |
michael@0 | 4824 | |
michael@0 | 4825 | static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true) |
michael@0 | 4826 | { |
michael@0 | 4827 | if (aHasDisplayItem) { |
michael@0 | 4828 | aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT); |
michael@0 | 4829 | } |
michael@0 | 4830 | nsSVGEffects::InvalidateDirectRenderingObservers(aFrame); |
michael@0 | 4831 | bool needsSchedulePaint = false; |
michael@0 | 4832 | if (nsLayoutUtils::IsPopup(aFrame)) { |
michael@0 | 4833 | needsSchedulePaint = true; |
michael@0 | 4834 | } else { |
michael@0 | 4835 | nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); |
michael@0 | 4836 | while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { |
michael@0 | 4837 | if (aHasDisplayItem) { |
michael@0 | 4838 | parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
michael@0 | 4839 | } |
michael@0 | 4840 | nsSVGEffects::InvalidateDirectRenderingObservers(parent); |
michael@0 | 4841 | |
michael@0 | 4842 | // If we're inside a popup, then we need to make sure that we |
michael@0 | 4843 | // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE |
michael@0 | 4844 | // flag gets added to the popup display root frame. |
michael@0 | 4845 | if (nsLayoutUtils::IsPopup(parent)) { |
michael@0 | 4846 | needsSchedulePaint = true; |
michael@0 | 4847 | break; |
michael@0 | 4848 | } |
michael@0 | 4849 | parent = nsLayoutUtils::GetCrossDocParentFrame(parent); |
michael@0 | 4850 | } |
michael@0 | 4851 | if (!parent) { |
michael@0 | 4852 | needsSchedulePaint = true; |
michael@0 | 4853 | } |
michael@0 | 4854 | } |
michael@0 | 4855 | if (!aHasDisplayItem) { |
michael@0 | 4856 | return; |
michael@0 | 4857 | } |
michael@0 | 4858 | if (needsSchedulePaint) { |
michael@0 | 4859 | aFrame->SchedulePaint(); |
michael@0 | 4860 | } |
michael@0 | 4861 | if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { |
michael@0 | 4862 | aFrame->Properties().Delete(nsIFrame::InvalidationRect()); |
michael@0 | 4863 | aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT); |
michael@0 | 4864 | } |
michael@0 | 4865 | } |
michael@0 | 4866 | |
michael@0 | 4867 | void |
michael@0 | 4868 | nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey) |
michael@0 | 4869 | { |
michael@0 | 4870 | bool hasDisplayItem = |
michael@0 | 4871 | !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); |
michael@0 | 4872 | InvalidateFrame(aDisplayItemKey); |
michael@0 | 4873 | |
michael@0 | 4874 | if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT) || !hasDisplayItem) { |
michael@0 | 4875 | return; |
michael@0 | 4876 | } |
michael@0 | 4877 | |
michael@0 | 4878 | AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT); |
michael@0 | 4879 | |
michael@0 | 4880 | nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
michael@0 | 4881 | GetCrossDocChildLists(&childListArray); |
michael@0 | 4882 | |
michael@0 | 4883 | nsIFrame::ChildListArrayIterator lists(childListArray); |
michael@0 | 4884 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 4885 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 4886 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 4887 | childFrames.get()->InvalidateFrameSubtree(); |
michael@0 | 4888 | } |
michael@0 | 4889 | } |
michael@0 | 4890 | } |
michael@0 | 4891 | |
michael@0 | 4892 | void |
michael@0 | 4893 | nsIFrame::ClearInvalidationStateBits() |
michael@0 | 4894 | { |
michael@0 | 4895 | if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { |
michael@0 | 4896 | nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
michael@0 | 4897 | GetCrossDocChildLists(&childListArray); |
michael@0 | 4898 | |
michael@0 | 4899 | nsIFrame::ChildListArrayIterator lists(childListArray); |
michael@0 | 4900 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 4901 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 4902 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 4903 | childFrames.get()->ClearInvalidationStateBits(); |
michael@0 | 4904 | } |
michael@0 | 4905 | } |
michael@0 | 4906 | } |
michael@0 | 4907 | |
michael@0 | 4908 | RemoveStateBits(NS_FRAME_NEEDS_PAINT | |
michael@0 | 4909 | NS_FRAME_DESCENDANT_NEEDS_PAINT | |
michael@0 | 4910 | NS_FRAME_ALL_DESCENDANTS_NEED_PAINT); |
michael@0 | 4911 | } |
michael@0 | 4912 | |
michael@0 | 4913 | void |
michael@0 | 4914 | nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey) |
michael@0 | 4915 | { |
michael@0 | 4916 | bool hasDisplayItem = |
michael@0 | 4917 | !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); |
michael@0 | 4918 | InvalidateFrameInternal(this, hasDisplayItem); |
michael@0 | 4919 | } |
michael@0 | 4920 | |
michael@0 | 4921 | void |
michael@0 | 4922 | nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) |
michael@0 | 4923 | { |
michael@0 | 4924 | bool hasDisplayItem = |
michael@0 | 4925 | !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); |
michael@0 | 4926 | bool alreadyInvalid = false; |
michael@0 | 4927 | if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { |
michael@0 | 4928 | InvalidateFrameInternal(this, hasDisplayItem); |
michael@0 | 4929 | } else { |
michael@0 | 4930 | alreadyInvalid = true; |
michael@0 | 4931 | } |
michael@0 | 4932 | |
michael@0 | 4933 | if (!hasDisplayItem) { |
michael@0 | 4934 | return; |
michael@0 | 4935 | } |
michael@0 | 4936 | |
michael@0 | 4937 | nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect())); |
michael@0 | 4938 | if (!rect) { |
michael@0 | 4939 | if (alreadyInvalid) { |
michael@0 | 4940 | return; |
michael@0 | 4941 | } |
michael@0 | 4942 | rect = new nsRect(); |
michael@0 | 4943 | Properties().Set(InvalidationRect(), rect); |
michael@0 | 4944 | AddStateBits(NS_FRAME_HAS_INVALID_RECT); |
michael@0 | 4945 | } |
michael@0 | 4946 | |
michael@0 | 4947 | *rect = rect->Union(aRect); |
michael@0 | 4948 | } |
michael@0 | 4949 | |
michael@0 | 4950 | /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey; |
michael@0 | 4951 | |
michael@0 | 4952 | bool |
michael@0 | 4953 | nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult) |
michael@0 | 4954 | { |
michael@0 | 4955 | Layer* layer = FrameLayerBuilder::GetDedicatedLayer( |
michael@0 | 4956 | this, nsDisplayItem::TYPE_TRANSFORM); |
michael@0 | 4957 | if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) { |
michael@0 | 4958 | // If this layer isn't prerendered or we clip composites to our OS |
michael@0 | 4959 | // window, then we can't correctly optimize to an empty |
michael@0 | 4960 | // transaction in general. |
michael@0 | 4961 | return false; |
michael@0 | 4962 | } |
michael@0 | 4963 | |
michael@0 | 4964 | gfx3DMatrix transform3d; |
michael@0 | 4965 | if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) { |
michael@0 | 4966 | // We're not able to compute a layer transform that we know would |
michael@0 | 4967 | // be used at the next layers transaction, so we can't only update |
michael@0 | 4968 | // the transform and will need to schedule an invalidating paint. |
michael@0 | 4969 | return false; |
michael@0 | 4970 | } |
michael@0 | 4971 | gfxMatrix transform; |
michael@0 | 4972 | gfx::Matrix previousTransform; |
michael@0 | 4973 | // FIXME/bug 796690 and 796705: in general, changes to 3D |
michael@0 | 4974 | // transforms, or transform changes to properties other than |
michael@0 | 4975 | // translation, may lead us to choose a different rendering |
michael@0 | 4976 | // resolution for our layer. So if the transform is 3D or has a |
michael@0 | 4977 | // non-translation change, bail and schedule an invalidating paint. |
michael@0 | 4978 | // (We can often do better than this, for example for scale-down |
michael@0 | 4979 | // changes.) |
michael@0 | 4980 | static const gfx::Float kError = 0.0001f; |
michael@0 | 4981 | if (!transform3d.Is2D(&transform) || |
michael@0 | 4982 | !layer->GetBaseTransform().Is2D(&previousTransform) || |
michael@0 | 4983 | !gfx::FuzzyEqual(transform.xx, previousTransform._11, kError) || |
michael@0 | 4984 | !gfx::FuzzyEqual(transform.yy, previousTransform._22, kError) || |
michael@0 | 4985 | !gfx::FuzzyEqual(transform.xy, previousTransform._21, kError) || |
michael@0 | 4986 | !gfx::FuzzyEqual(transform.yx, previousTransform._12, kError)) { |
michael@0 | 4987 | return false; |
michael@0 | 4988 | } |
michael@0 | 4989 | gfx::Matrix4x4 matrix; |
michael@0 | 4990 | gfx::ToMatrix4x4(transform3d, matrix); |
michael@0 | 4991 | layer->SetBaseTransformForNextTransaction(matrix); |
michael@0 | 4992 | *aLayerResult = layer; |
michael@0 | 4993 | return true; |
michael@0 | 4994 | } |
michael@0 | 4995 | |
michael@0 | 4996 | bool |
michael@0 | 4997 | nsIFrame::IsInvalid(nsRect& aRect) |
michael@0 | 4998 | { |
michael@0 | 4999 | if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { |
michael@0 | 5000 | return false; |
michael@0 | 5001 | } |
michael@0 | 5002 | |
michael@0 | 5003 | if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { |
michael@0 | 5004 | nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect())); |
michael@0 | 5005 | NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!"); |
michael@0 | 5006 | aRect = *rect; |
michael@0 | 5007 | } else { |
michael@0 | 5008 | aRect.SetEmpty(); |
michael@0 | 5009 | } |
michael@0 | 5010 | return true; |
michael@0 | 5011 | } |
michael@0 | 5012 | |
michael@0 | 5013 | void |
michael@0 | 5014 | nsIFrame::SchedulePaint(PaintType aType) |
michael@0 | 5015 | { |
michael@0 | 5016 | nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this); |
michael@0 | 5017 | nsPresContext *pres = displayRoot->PresContext()->GetRootPresContext(); |
michael@0 | 5018 | |
michael@0 | 5019 | // No need to schedule a paint for an external document since they aren't |
michael@0 | 5020 | // painted directly. |
michael@0 | 5021 | if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) { |
michael@0 | 5022 | return; |
michael@0 | 5023 | } |
michael@0 | 5024 | if (!pres->GetContainerWeak()) { |
michael@0 | 5025 | NS_WARNING("Shouldn't call SchedulePaint in a detached pres context"); |
michael@0 | 5026 | return; |
michael@0 | 5027 | } |
michael@0 | 5028 | |
michael@0 | 5029 | pres->PresShell()->ScheduleViewManagerFlush(aType == PAINT_DELAYED_COMPRESS ? |
michael@0 | 5030 | nsIPresShell::PAINT_DELAYED_COMPRESS : |
michael@0 | 5031 | nsIPresShell::PAINT_DEFAULT); |
michael@0 | 5032 | |
michael@0 | 5033 | if (aType == PAINT_DELAYED_COMPRESS) { |
michael@0 | 5034 | return; |
michael@0 | 5035 | } |
michael@0 | 5036 | |
michael@0 | 5037 | if (aType == PAINT_DEFAULT) { |
michael@0 | 5038 | displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE); |
michael@0 | 5039 | } |
michael@0 | 5040 | nsIPresShell* shell = PresContext()->PresShell(); |
michael@0 | 5041 | if (shell) { |
michael@0 | 5042 | shell->AddInvalidateHiddenPresShellObserver(pres->RefreshDriver()); |
michael@0 | 5043 | } |
michael@0 | 5044 | } |
michael@0 | 5045 | |
michael@0 | 5046 | Layer* |
michael@0 | 5047 | nsIFrame::InvalidateLayer(uint32_t aDisplayItemKey, |
michael@0 | 5048 | const nsIntRect* aDamageRect, |
michael@0 | 5049 | uint32_t aFlags /* = 0 */) |
michael@0 | 5050 | { |
michael@0 | 5051 | NS_ASSERTION(aDisplayItemKey > 0, "Need a key"); |
michael@0 | 5052 | |
michael@0 | 5053 | Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey); |
michael@0 | 5054 | |
michael@0 | 5055 | // If the layer is being updated asynchronously, and it's being forwarded |
michael@0 | 5056 | // to a compositor, then we don't need to invalidate. |
michael@0 | 5057 | if ((aFlags & UPDATE_IS_ASYNC) && layer && |
michael@0 | 5058 | layer->Manager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { |
michael@0 | 5059 | return layer; |
michael@0 | 5060 | } |
michael@0 | 5061 | |
michael@0 | 5062 | if (aDamageRect && aDamageRect->IsEmpty()) { |
michael@0 | 5063 | return layer; |
michael@0 | 5064 | } |
michael@0 | 5065 | |
michael@0 | 5066 | if (!layer) { |
michael@0 | 5067 | // Plugins can transition from not rendering anything to rendering, |
michael@0 | 5068 | // and still only call this. So always invalidate, with specifying |
michael@0 | 5069 | // the display item type just in case. |
michael@0 | 5070 | // |
michael@0 | 5071 | // In the bug 930056, dialer app startup but not shown on the |
michael@0 | 5072 | // screen because sometimes we don't have any retainned data |
michael@0 | 5073 | // for remote type displayitem and thus Repaint event is not |
michael@0 | 5074 | // triggered. So, always invalidate here as well. |
michael@0 | 5075 | if (aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN || |
michael@0 | 5076 | aDisplayItemKey == nsDisplayItem::TYPE_REMOTE) { |
michael@0 | 5077 | InvalidateFrame(); |
michael@0 | 5078 | } else { |
michael@0 | 5079 | InvalidateFrame(aDisplayItemKey); |
michael@0 | 5080 | } |
michael@0 | 5081 | return nullptr; |
michael@0 | 5082 | } |
michael@0 | 5083 | |
michael@0 | 5084 | if (aDamageRect) { |
michael@0 | 5085 | layer->AddInvalidRect(*aDamageRect); |
michael@0 | 5086 | } else { |
michael@0 | 5087 | layer->SetInvalidRectToVisibleRegion(); |
michael@0 | 5088 | } |
michael@0 | 5089 | |
michael@0 | 5090 | SchedulePaint(PAINT_COMPOSITE_ONLY); |
michael@0 | 5091 | return layer; |
michael@0 | 5092 | } |
michael@0 | 5093 | |
michael@0 | 5094 | static nsRect |
michael@0 | 5095 | ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect, |
michael@0 | 5096 | const nsSize& aNewSize) |
michael@0 | 5097 | { |
michael@0 | 5098 | nsRect r = aOverflowRect; |
michael@0 | 5099 | |
michael@0 | 5100 | if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
michael@0 | 5101 | // For SVG frames, we only need to account for filters. |
michael@0 | 5102 | // TODO: We could also take account of clipPath and mask to reduce the |
michael@0 | 5103 | // visual overflow, but that's not essential. |
michael@0 | 5104 | if (aFrame->StyleSVGReset()->HasFilters()) { |
michael@0 | 5105 | aFrame->Properties(). |
michael@0 | 5106 | Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); |
michael@0 | 5107 | r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect); |
michael@0 | 5108 | } |
michael@0 | 5109 | return r; |
michael@0 | 5110 | } |
michael@0 | 5111 | |
michael@0 | 5112 | // box-shadow |
michael@0 | 5113 | r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize)); |
michael@0 | 5114 | |
michael@0 | 5115 | // border-image-outset. |
michael@0 | 5116 | // We need to include border-image-outset because it can cause the |
michael@0 | 5117 | // border image to be drawn beyond the border box. |
michael@0 | 5118 | |
michael@0 | 5119 | // (1) It's important we not check whether there's a border-image |
michael@0 | 5120 | // since the style hint for a change in border image doesn't cause |
michael@0 | 5121 | // reflow, and that's probably more important than optimizing the |
michael@0 | 5122 | // overflow areas for the silly case of border-image-outset without |
michael@0 | 5123 | // border-image |
michael@0 | 5124 | // (2) It's important that we not check whether the border-image |
michael@0 | 5125 | // is actually loaded, since that would require us to reflow when |
michael@0 | 5126 | // the image loads. |
michael@0 | 5127 | const nsStyleBorder* styleBorder = aFrame->StyleBorder(); |
michael@0 | 5128 | nsMargin outsetMargin = styleBorder->GetImageOutset(); |
michael@0 | 5129 | |
michael@0 | 5130 | if (outsetMargin != nsMargin(0, 0, 0, 0)) { |
michael@0 | 5131 | nsRect outsetRect(nsPoint(0, 0), aNewSize); |
michael@0 | 5132 | outsetRect.Inflate(outsetMargin); |
michael@0 | 5133 | r.UnionRect(r, outsetRect); |
michael@0 | 5134 | } |
michael@0 | 5135 | |
michael@0 | 5136 | // Note that we don't remove the outlineInnerRect if a frame loses outline |
michael@0 | 5137 | // style. That would require an extra property lookup for every frame, |
michael@0 | 5138 | // or a new frame state bit to track whether a property had been stored, |
michael@0 | 5139 | // or something like that. It's not worth doing that here. At most it's |
michael@0 | 5140 | // only one heap-allocated rect per frame and it will be cleaned up when |
michael@0 | 5141 | // the frame dies. |
michael@0 | 5142 | |
michael@0 | 5143 | if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) { |
michael@0 | 5144 | aFrame->Properties(). |
michael@0 | 5145 | Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); |
michael@0 | 5146 | r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r); |
michael@0 | 5147 | } |
michael@0 | 5148 | |
michael@0 | 5149 | return r; |
michael@0 | 5150 | } |
michael@0 | 5151 | |
michael@0 | 5152 | void |
michael@0 | 5153 | nsIFrame::MovePositionBy(const nsPoint& aTranslation) |
michael@0 | 5154 | { |
michael@0 | 5155 | nsPoint position = GetNormalPosition() + aTranslation; |
michael@0 | 5156 | |
michael@0 | 5157 | const nsMargin* computedOffsets = nullptr; |
michael@0 | 5158 | if (IsRelativelyPositioned()) { |
michael@0 | 5159 | computedOffsets = static_cast<nsMargin*> |
michael@0 | 5160 | (Properties().Get(nsIFrame::ComputedOffsetProperty())); |
michael@0 | 5161 | } |
michael@0 | 5162 | nsHTMLReflowState::ApplyRelativePositioning(this, computedOffsets ? |
michael@0 | 5163 | *computedOffsets : nsMargin(), |
michael@0 | 5164 | &position); |
michael@0 | 5165 | NS_ASSERTION(StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY || |
michael@0 | 5166 | GetPosition() + aTranslation == position, |
michael@0 | 5167 | "MovePositionBy should always lead to the movement " |
michael@0 | 5168 | "specified, unless the frame is position:sticky"); |
michael@0 | 5169 | SetPosition(position); |
michael@0 | 5170 | } |
michael@0 | 5171 | |
michael@0 | 5172 | nsPoint |
michael@0 | 5173 | nsIFrame::GetNormalPosition() const |
michael@0 | 5174 | { |
michael@0 | 5175 | // It might be faster to first check |
michael@0 | 5176 | // StyleDisplay()->IsRelativelyPositionedStyle(). |
michael@0 | 5177 | nsPoint* normalPosition = static_cast<nsPoint*> |
michael@0 | 5178 | (Properties().Get(NormalPositionProperty())); |
michael@0 | 5179 | if (normalPosition) { |
michael@0 | 5180 | return *normalPosition; |
michael@0 | 5181 | } |
michael@0 | 5182 | return GetPosition(); |
michael@0 | 5183 | } |
michael@0 | 5184 | |
michael@0 | 5185 | nsRect |
michael@0 | 5186 | nsIFrame::GetOverflowRect(nsOverflowType aType) const |
michael@0 | 5187 | { |
michael@0 | 5188 | NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow, |
michael@0 | 5189 | "unexpected type"); |
michael@0 | 5190 | |
michael@0 | 5191 | // Note that in some cases the overflow area might not have been |
michael@0 | 5192 | // updated (yet) to reflect any outline set on the frame or the area |
michael@0 | 5193 | // of child frames. That's OK because any reflow that updates these |
michael@0 | 5194 | // areas will invalidate the appropriate area, so any (mis)uses of |
michael@0 | 5195 | // this method will be fixed up. |
michael@0 | 5196 | |
michael@0 | 5197 | if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
michael@0 | 5198 | // there is an overflow rect, and it's not stored as deltas but as |
michael@0 | 5199 | // a separately-allocated rect |
michael@0 | 5200 | return static_cast<nsOverflowAreas*>(const_cast<nsIFrame*>(this)-> |
michael@0 | 5201 | GetOverflowAreasProperty())->Overflow(aType); |
michael@0 | 5202 | } |
michael@0 | 5203 | |
michael@0 | 5204 | if (aType == eVisualOverflow && |
michael@0 | 5205 | mOverflow.mType != NS_FRAME_OVERFLOW_NONE) { |
michael@0 | 5206 | return GetVisualOverflowFromDeltas(); |
michael@0 | 5207 | } |
michael@0 | 5208 | |
michael@0 | 5209 | return nsRect(nsPoint(0, 0), GetSize()); |
michael@0 | 5210 | } |
michael@0 | 5211 | |
michael@0 | 5212 | nsOverflowAreas |
michael@0 | 5213 | nsIFrame::GetOverflowAreas() const |
michael@0 | 5214 | { |
michael@0 | 5215 | if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
michael@0 | 5216 | // there is an overflow rect, and it's not stored as deltas but as |
michael@0 | 5217 | // a separately-allocated rect |
michael@0 | 5218 | return *const_cast<nsIFrame*>(this)->GetOverflowAreasProperty(); |
michael@0 | 5219 | } |
michael@0 | 5220 | |
michael@0 | 5221 | return nsOverflowAreas(GetVisualOverflowFromDeltas(), |
michael@0 | 5222 | nsRect(nsPoint(0, 0), GetSize())); |
michael@0 | 5223 | } |
michael@0 | 5224 | |
michael@0 | 5225 | nsOverflowAreas |
michael@0 | 5226 | nsIFrame::GetOverflowAreasRelativeToSelf() const |
michael@0 | 5227 | { |
michael@0 | 5228 | if (IsTransformed()) { |
michael@0 | 5229 | nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*> |
michael@0 | 5230 | (Properties().Get(PreTransformOverflowAreasProperty())); |
michael@0 | 5231 | if (preTransformOverflows) { |
michael@0 | 5232 | return nsOverflowAreas(preTransformOverflows->VisualOverflow(), |
michael@0 | 5233 | preTransformOverflows->ScrollableOverflow()); |
michael@0 | 5234 | } |
michael@0 | 5235 | } |
michael@0 | 5236 | return nsOverflowAreas(GetVisualOverflowRect(), |
michael@0 | 5237 | GetScrollableOverflowRect()); |
michael@0 | 5238 | } |
michael@0 | 5239 | |
michael@0 | 5240 | nsRect |
michael@0 | 5241 | nsIFrame::GetScrollableOverflowRectRelativeToParent() const |
michael@0 | 5242 | { |
michael@0 | 5243 | return GetScrollableOverflowRect() + mRect.TopLeft(); |
michael@0 | 5244 | } |
michael@0 | 5245 | |
michael@0 | 5246 | nsRect |
michael@0 | 5247 | nsIFrame::GetScrollableOverflowRectRelativeToSelf() const |
michael@0 | 5248 | { |
michael@0 | 5249 | if (IsTransformed()) { |
michael@0 | 5250 | nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*> |
michael@0 | 5251 | (Properties().Get(PreTransformOverflowAreasProperty())); |
michael@0 | 5252 | if (preTransformOverflows) |
michael@0 | 5253 | return preTransformOverflows->ScrollableOverflow(); |
michael@0 | 5254 | } |
michael@0 | 5255 | return GetScrollableOverflowRect(); |
michael@0 | 5256 | } |
michael@0 | 5257 | |
michael@0 | 5258 | nsRect |
michael@0 | 5259 | nsIFrame::GetVisualOverflowRectRelativeToSelf() const |
michael@0 | 5260 | { |
michael@0 | 5261 | if (IsTransformed()) { |
michael@0 | 5262 | nsOverflowAreas* preTransformOverflows = static_cast<nsOverflowAreas*> |
michael@0 | 5263 | (Properties().Get(PreTransformOverflowAreasProperty())); |
michael@0 | 5264 | if (preTransformOverflows) |
michael@0 | 5265 | return preTransformOverflows->VisualOverflow(); |
michael@0 | 5266 | } |
michael@0 | 5267 | return GetVisualOverflowRect(); |
michael@0 | 5268 | } |
michael@0 | 5269 | |
michael@0 | 5270 | nsRect |
michael@0 | 5271 | nsIFrame::GetPreEffectsVisualOverflowRect() const |
michael@0 | 5272 | { |
michael@0 | 5273 | nsRect* r = static_cast<nsRect*> |
michael@0 | 5274 | (Properties().Get(nsIFrame::PreEffectsBBoxProperty())); |
michael@0 | 5275 | return r ? *r : GetVisualOverflowRectRelativeToSelf(); |
michael@0 | 5276 | } |
michael@0 | 5277 | |
michael@0 | 5278 | inline static bool |
michael@0 | 5279 | FrameMaintainsOverflow(nsIFrame* aFrame) |
michael@0 | 5280 | { |
michael@0 | 5281 | return (aFrame->GetStateBits() & |
michael@0 | 5282 | (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY)) != |
michael@0 | 5283 | (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY); |
michael@0 | 5284 | } |
michael@0 | 5285 | |
michael@0 | 5286 | /* virtual */ bool |
michael@0 | 5287 | nsFrame::UpdateOverflow() |
michael@0 | 5288 | { |
michael@0 | 5289 | MOZ_ASSERT(FrameMaintainsOverflow(this), |
michael@0 | 5290 | "Non-display SVG do not maintain visual overflow rects"); |
michael@0 | 5291 | |
michael@0 | 5292 | nsRect rect(nsPoint(0, 0), GetSize()); |
michael@0 | 5293 | nsOverflowAreas overflowAreas(rect, rect); |
michael@0 | 5294 | |
michael@0 | 5295 | if (!DoesClipChildren() && |
michael@0 | 5296 | !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) { |
michael@0 | 5297 | nsLayoutUtils::UnionChildOverflow(this, overflowAreas); |
michael@0 | 5298 | } |
michael@0 | 5299 | |
michael@0 | 5300 | if (FinishAndStoreOverflow(overflowAreas, GetSize())) { |
michael@0 | 5301 | nsView* view = GetView(); |
michael@0 | 5302 | if (view) { |
michael@0 | 5303 | uint32_t flags = 0; |
michael@0 | 5304 | GetLayoutFlags(flags); |
michael@0 | 5305 | |
michael@0 | 5306 | if ((flags & NS_FRAME_NO_SIZE_VIEW) == 0) { |
michael@0 | 5307 | // Make sure the frame's view is properly sized. |
michael@0 | 5308 | nsViewManager* vm = view->GetViewManager(); |
michael@0 | 5309 | vm->ResizeView(view, overflowAreas.VisualOverflow(), true); |
michael@0 | 5310 | } |
michael@0 | 5311 | } |
michael@0 | 5312 | |
michael@0 | 5313 | return true; |
michael@0 | 5314 | } |
michael@0 | 5315 | |
michael@0 | 5316 | return false; |
michael@0 | 5317 | } |
michael@0 | 5318 | |
michael@0 | 5319 | // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus |
michael@0 | 5320 | // 4 for the frames above the document's frames: |
michael@0 | 5321 | // the Viewport, GFXScroll, ScrollPort, and Canvas |
michael@0 | 5322 | #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4) |
michael@0 | 5323 | |
michael@0 | 5324 | bool |
michael@0 | 5325 | nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState, |
michael@0 | 5326 | nsHTMLReflowMetrics& aMetrics, |
michael@0 | 5327 | nsReflowStatus& aStatus) |
michael@0 | 5328 | { |
michael@0 | 5329 | if (aReflowState.mReflowDepth > MAX_FRAME_DEPTH) { |
michael@0 | 5330 | NS_WARNING("frame tree too deep; setting zero size and returning"); |
michael@0 | 5331 | mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE; |
michael@0 | 5332 | ClearOverflowRects(); |
michael@0 | 5333 | aMetrics.Width() = 0; |
michael@0 | 5334 | aMetrics.Height() = 0; |
michael@0 | 5335 | aMetrics.SetTopAscent(0); |
michael@0 | 5336 | aMetrics.mCarriedOutBottomMargin.Zero(); |
michael@0 | 5337 | aMetrics.mOverflowAreas.Clear(); |
michael@0 | 5338 | |
michael@0 | 5339 | if (GetNextInFlow()) { |
michael@0 | 5340 | // Reflow depth might vary between reflows, so we might have |
michael@0 | 5341 | // successfully reflowed and split this frame before. If so, we |
michael@0 | 5342 | // shouldn't delete its continuations. |
michael@0 | 5343 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 5344 | } else { |
michael@0 | 5345 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 5346 | } |
michael@0 | 5347 | |
michael@0 | 5348 | return true; |
michael@0 | 5349 | } |
michael@0 | 5350 | mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE; |
michael@0 | 5351 | return false; |
michael@0 | 5352 | } |
michael@0 | 5353 | |
michael@0 | 5354 | bool |
michael@0 | 5355 | nsIFrame::IsBlockWrapper() const |
michael@0 | 5356 | { |
michael@0 | 5357 | nsIAtom *pseudoType = StyleContext()->GetPseudo(); |
michael@0 | 5358 | return (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || |
michael@0 | 5359 | pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || |
michael@0 | 5360 | pseudoType == nsCSSAnonBoxes::buttonContent || |
michael@0 | 5361 | pseudoType == nsCSSAnonBoxes::cellContent); |
michael@0 | 5362 | } |
michael@0 | 5363 | |
michael@0 | 5364 | static nsIFrame* |
michael@0 | 5365 | GetNearestBlockContainer(nsIFrame* frame) |
michael@0 | 5366 | { |
michael@0 | 5367 | // The block wrappers we use to wrap blocks inside inlines aren't |
michael@0 | 5368 | // described in the CSS spec. We need to make them not be containing |
michael@0 | 5369 | // blocks. |
michael@0 | 5370 | // Since the parent of such a block is either a normal block or |
michael@0 | 5371 | // another such pseudo, this shouldn't cause anything bad to happen. |
michael@0 | 5372 | // Also the anonymous blocks inside table cells are not containing blocks. |
michael@0 | 5373 | while (frame->IsFrameOfType(nsIFrame::eLineParticipant) || |
michael@0 | 5374 | frame->IsBlockWrapper() || |
michael@0 | 5375 | // Table rows are not containing blocks either |
michael@0 | 5376 | frame->GetType() == nsGkAtoms::tableRowFrame) { |
michael@0 | 5377 | frame = frame->GetParent(); |
michael@0 | 5378 | NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?"); |
michael@0 | 5379 | } |
michael@0 | 5380 | return frame; |
michael@0 | 5381 | } |
michael@0 | 5382 | |
michael@0 | 5383 | nsIFrame* |
michael@0 | 5384 | nsIFrame::GetContainingBlock() const |
michael@0 | 5385 | { |
michael@0 | 5386 | // MathML frames might have absolute positioning style, but they would |
michael@0 | 5387 | // still be in-flow. So we have to check to make sure that the frame |
michael@0 | 5388 | // is really out-of-flow too. |
michael@0 | 5389 | if (IsAbsolutelyPositioned() && |
michael@0 | 5390 | (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { |
michael@0 | 5391 | return GetParent(); // the parent is always the containing block |
michael@0 | 5392 | } |
michael@0 | 5393 | return GetNearestBlockContainer(GetParent()); |
michael@0 | 5394 | } |
michael@0 | 5395 | |
michael@0 | 5396 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 5397 | |
michael@0 | 5398 | int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame) |
michael@0 | 5399 | { |
michael@0 | 5400 | int32_t result = -1; |
michael@0 | 5401 | |
michael@0 | 5402 | nsIContent* content = aFrame->GetContent(); |
michael@0 | 5403 | if (content) { |
michael@0 | 5404 | nsIContent* parentContent = content->GetParent(); |
michael@0 | 5405 | if (parentContent) { |
michael@0 | 5406 | result = parentContent->IndexOf(content); |
michael@0 | 5407 | } |
michael@0 | 5408 | } |
michael@0 | 5409 | |
michael@0 | 5410 | return result; |
michael@0 | 5411 | } |
michael@0 | 5412 | |
michael@0 | 5413 | /** |
michael@0 | 5414 | * List a frame tree to stderr. Meant to be called from gdb. |
michael@0 | 5415 | */ |
michael@0 | 5416 | void |
michael@0 | 5417 | DebugListFrameTree(nsIFrame* aFrame) |
michael@0 | 5418 | { |
michael@0 | 5419 | ((nsFrame*)aFrame)->List(stderr); |
michael@0 | 5420 | } |
michael@0 | 5421 | |
michael@0 | 5422 | void |
michael@0 | 5423 | nsIFrame::ListTag(nsACString& aTo) const |
michael@0 | 5424 | { |
michael@0 | 5425 | ListTag(aTo, this); |
michael@0 | 5426 | } |
michael@0 | 5427 | |
michael@0 | 5428 | /* static */ |
michael@0 | 5429 | void |
michael@0 | 5430 | nsIFrame::ListTag(nsACString& aTo, const nsIFrame* aFrame) { |
michael@0 | 5431 | nsAutoString tmp; |
michael@0 | 5432 | aFrame->GetFrameName(tmp); |
michael@0 | 5433 | aTo += NS_ConvertUTF16toUTF8(tmp).get(); |
michael@0 | 5434 | aTo += nsPrintfCString("@%p", static_cast<const void*>(aFrame)); |
michael@0 | 5435 | } |
michael@0 | 5436 | |
michael@0 | 5437 | // Debugging |
michael@0 | 5438 | void |
michael@0 | 5439 | nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) const |
michael@0 | 5440 | { |
michael@0 | 5441 | aTo =+ aPrefix; |
michael@0 | 5442 | ListTag(aTo); |
michael@0 | 5443 | if (HasView()) { |
michael@0 | 5444 | aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView())); |
michael@0 | 5445 | } |
michael@0 | 5446 | if (GetNextSibling()) { |
michael@0 | 5447 | aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling())); |
michael@0 | 5448 | } |
michael@0 | 5449 | if (GetPrevContinuation()) { |
michael@0 | 5450 | bool fluid = GetPrevInFlow() == GetPrevContinuation(); |
michael@0 | 5451 | aTo += nsPrintfCString(" prev-%s=%p", fluid?"in-flow":"continuation", |
michael@0 | 5452 | static_cast<void*>(GetPrevContinuation())); |
michael@0 | 5453 | } |
michael@0 | 5454 | if (GetNextContinuation()) { |
michael@0 | 5455 | bool fluid = GetNextInFlow() == GetNextContinuation(); |
michael@0 | 5456 | aTo += nsPrintfCString(" next-%s=%p", fluid?"in-flow":"continuation", |
michael@0 | 5457 | static_cast<void*>(GetNextContinuation())); |
michael@0 | 5458 | } |
michael@0 | 5459 | void* IBsibling = Properties().Get(IBSplitSibling()); |
michael@0 | 5460 | if (IBsibling) { |
michael@0 | 5461 | aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling); |
michael@0 | 5462 | } |
michael@0 | 5463 | void* IBprevsibling = Properties().Get(IBSplitPrevSibling()); |
michael@0 | 5464 | if (IBprevsibling) { |
michael@0 | 5465 | aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling); |
michael@0 | 5466 | } |
michael@0 | 5467 | aTo += nsPrintfCString(" {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); |
michael@0 | 5468 | nsIFrame* f = const_cast<nsIFrame*>(this); |
michael@0 | 5469 | if (f->HasOverflowAreas()) { |
michael@0 | 5470 | nsRect vo = f->GetVisualOverflowRect(); |
michael@0 | 5471 | if (!vo.IsEqualEdges(mRect)) { |
michael@0 | 5472 | aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height); |
michael@0 | 5473 | } |
michael@0 | 5474 | nsRect so = f->GetScrollableOverflowRect(); |
michael@0 | 5475 | if (!so.IsEqualEdges(mRect)) { |
michael@0 | 5476 | aTo += nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so.x, so.y, so.width, so.height); |
michael@0 | 5477 | } |
michael@0 | 5478 | } |
michael@0 | 5479 | if (0 != mState) { |
michael@0 | 5480 | aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState); |
michael@0 | 5481 | } |
michael@0 | 5482 | if (IsTransformed()) { |
michael@0 | 5483 | aTo += nsPrintfCString(" transformed"); |
michael@0 | 5484 | } |
michael@0 | 5485 | if (ChildrenHavePerspective()) { |
michael@0 | 5486 | aTo += nsPrintfCString(" perspective"); |
michael@0 | 5487 | } |
michael@0 | 5488 | if (Preserves3DChildren()) { |
michael@0 | 5489 | aTo += nsPrintfCString(" preserves-3d-children"); |
michael@0 | 5490 | } |
michael@0 | 5491 | if (Preserves3D()) { |
michael@0 | 5492 | aTo += nsPrintfCString(" preserves-3d"); |
michael@0 | 5493 | } |
michael@0 | 5494 | if (mContent) { |
michael@0 | 5495 | aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent)); |
michael@0 | 5496 | } |
michael@0 | 5497 | aTo += nsPrintfCString(" [sc=%p", static_cast<void*>(mStyleContext)); |
michael@0 | 5498 | if (mStyleContext) { |
michael@0 | 5499 | nsIAtom* pseudoTag = mStyleContext->GetPseudo(); |
michael@0 | 5500 | if (pseudoTag) { |
michael@0 | 5501 | nsAutoString atomString; |
michael@0 | 5502 | pseudoTag->ToString(atomString); |
michael@0 | 5503 | aTo += nsPrintfCString("%s", NS_LossyConvertUTF16toASCII(atomString).get()); |
michael@0 | 5504 | } |
michael@0 | 5505 | if (mParent && mStyleContext->GetParent() != mParent->StyleContext()) { |
michael@0 | 5506 | aTo += nsPrintfCString(",parent=%p", mStyleContext->GetParent()); |
michael@0 | 5507 | } |
michael@0 | 5508 | } |
michael@0 | 5509 | aTo += "]"; |
michael@0 | 5510 | } |
michael@0 | 5511 | |
michael@0 | 5512 | void |
michael@0 | 5513 | nsIFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
michael@0 | 5514 | { |
michael@0 | 5515 | nsCString str; |
michael@0 | 5516 | ListGeneric(str, aPrefix, aFlags); |
michael@0 | 5517 | fprintf_stderr(out, "%s\n", str.get()); |
michael@0 | 5518 | } |
michael@0 | 5519 | |
michael@0 | 5520 | nsresult |
michael@0 | 5521 | nsFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 5522 | { |
michael@0 | 5523 | return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult); |
michael@0 | 5524 | } |
michael@0 | 5525 | |
michael@0 | 5526 | nsresult |
michael@0 | 5527 | nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const |
michael@0 | 5528 | { |
michael@0 | 5529 | aResult = aType; |
michael@0 | 5530 | if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) { |
michael@0 | 5531 | nsAutoString buf; |
michael@0 | 5532 | mContent->Tag()->ToString(buf); |
michael@0 | 5533 | if (GetType() == nsGkAtoms::subDocumentFrame) { |
michael@0 | 5534 | nsAutoString src; |
michael@0 | 5535 | mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); |
michael@0 | 5536 | buf.Append(NS_LITERAL_STRING(" src=") + src); |
michael@0 | 5537 | } |
michael@0 | 5538 | aResult.Append(NS_LITERAL_STRING("(") + buf + NS_LITERAL_STRING(")")); |
michael@0 | 5539 | } |
michael@0 | 5540 | char buf[40]; |
michael@0 | 5541 | PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this)); |
michael@0 | 5542 | AppendASCIItoUTF16(buf, aResult); |
michael@0 | 5543 | return NS_OK; |
michael@0 | 5544 | } |
michael@0 | 5545 | |
michael@0 | 5546 | void |
michael@0 | 5547 | nsIFrame::DumpFrameTree() |
michael@0 | 5548 | { |
michael@0 | 5549 | RootFrameList(PresContext(), stderr); |
michael@0 | 5550 | } |
michael@0 | 5551 | |
michael@0 | 5552 | void |
michael@0 | 5553 | nsIFrame::DumpFrameTreeLimited() |
michael@0 | 5554 | { |
michael@0 | 5555 | List(stderr); |
michael@0 | 5556 | } |
michael@0 | 5557 | |
michael@0 | 5558 | void |
michael@0 | 5559 | nsIFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, const char* aPrefix) |
michael@0 | 5560 | { |
michael@0 | 5561 | if (!aPresContext || !out) |
michael@0 | 5562 | return; |
michael@0 | 5563 | |
michael@0 | 5564 | nsIPresShell *shell = aPresContext->GetPresShell(); |
michael@0 | 5565 | if (shell) { |
michael@0 | 5566 | nsIFrame* frame = shell->FrameManager()->GetRootFrame(); |
michael@0 | 5567 | if(frame) { |
michael@0 | 5568 | frame->List(out, aPrefix); |
michael@0 | 5569 | } |
michael@0 | 5570 | } |
michael@0 | 5571 | } |
michael@0 | 5572 | #endif |
michael@0 | 5573 | |
michael@0 | 5574 | #ifdef DEBUG |
michael@0 | 5575 | nsFrameState |
michael@0 | 5576 | nsFrame::GetDebugStateBits() const |
michael@0 | 5577 | { |
michael@0 | 5578 | // We'll ignore these flags for the purposes of comparing frame state: |
michael@0 | 5579 | // |
michael@0 | 5580 | // NS_FRAME_EXTERNAL_REFERENCE |
michael@0 | 5581 | // because this is set by the event state manager or the |
michael@0 | 5582 | // caret code when a frame is focused. Depending on whether |
michael@0 | 5583 | // or not the regression tests are run as the focused window |
michael@0 | 5584 | // will make this value vary randomly. |
michael@0 | 5585 | #define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE |
michael@0 | 5586 | |
michael@0 | 5587 | #define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS)) |
michael@0 | 5588 | |
michael@0 | 5589 | return GetStateBits() & FRAME_STATE_MASK; |
michael@0 | 5590 | } |
michael@0 | 5591 | |
michael@0 | 5592 | void |
michael@0 | 5593 | nsFrame::XMLQuote(nsString& aString) |
michael@0 | 5594 | { |
michael@0 | 5595 | int32_t i, len = aString.Length(); |
michael@0 | 5596 | for (i = 0; i < len; i++) { |
michael@0 | 5597 | char16_t ch = aString.CharAt(i); |
michael@0 | 5598 | if (ch == '<') { |
michael@0 | 5599 | nsAutoString tmp(NS_LITERAL_STRING("<")); |
michael@0 | 5600 | aString.Cut(i, 1); |
michael@0 | 5601 | aString.Insert(tmp, i); |
michael@0 | 5602 | len += 3; |
michael@0 | 5603 | i += 3; |
michael@0 | 5604 | } |
michael@0 | 5605 | else if (ch == '>') { |
michael@0 | 5606 | nsAutoString tmp(NS_LITERAL_STRING(">")); |
michael@0 | 5607 | aString.Cut(i, 1); |
michael@0 | 5608 | aString.Insert(tmp, i); |
michael@0 | 5609 | len += 3; |
michael@0 | 5610 | i += 3; |
michael@0 | 5611 | } |
michael@0 | 5612 | else if (ch == '\"') { |
michael@0 | 5613 | nsAutoString tmp(NS_LITERAL_STRING(""")); |
michael@0 | 5614 | aString.Cut(i, 1); |
michael@0 | 5615 | aString.Insert(tmp, i); |
michael@0 | 5616 | len += 5; |
michael@0 | 5617 | i += 5; |
michael@0 | 5618 | } |
michael@0 | 5619 | } |
michael@0 | 5620 | } |
michael@0 | 5621 | #endif |
michael@0 | 5622 | |
michael@0 | 5623 | bool |
michael@0 | 5624 | nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) { |
michael@0 | 5625 | if (!StyleVisibility()->IsVisible()) |
michael@0 | 5626 | return false; |
michael@0 | 5627 | nsISelection* sel = aBuilder->GetBoundingSelection(); |
michael@0 | 5628 | return !sel || IsVisibleInSelection(sel); |
michael@0 | 5629 | } |
michael@0 | 5630 | |
michael@0 | 5631 | bool |
michael@0 | 5632 | nsIFrame::IsVisibleForPainting() { |
michael@0 | 5633 | if (!StyleVisibility()->IsVisible()) |
michael@0 | 5634 | return false; |
michael@0 | 5635 | |
michael@0 | 5636 | nsPresContext* pc = PresContext(); |
michael@0 | 5637 | if (!pc->IsRenderingOnlySelection()) |
michael@0 | 5638 | return true; |
michael@0 | 5639 | |
michael@0 | 5640 | nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(pc->PresShell())); |
michael@0 | 5641 | if (selcon) { |
michael@0 | 5642 | nsCOMPtr<nsISelection> sel; |
michael@0 | 5643 | selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, |
michael@0 | 5644 | getter_AddRefs(sel)); |
michael@0 | 5645 | if (sel) |
michael@0 | 5646 | return IsVisibleInSelection(sel); |
michael@0 | 5647 | } |
michael@0 | 5648 | return true; |
michael@0 | 5649 | } |
michael@0 | 5650 | |
michael@0 | 5651 | bool |
michael@0 | 5652 | nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) { |
michael@0 | 5653 | nsISelection* sel = aBuilder->GetBoundingSelection(); |
michael@0 | 5654 | return !sel || IsVisibleInSelection(sel); |
michael@0 | 5655 | } |
michael@0 | 5656 | |
michael@0 | 5657 | bool |
michael@0 | 5658 | nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) { |
michael@0 | 5659 | if (!StyleVisibility()->IsVisibleOrCollapsed()) |
michael@0 | 5660 | return false; |
michael@0 | 5661 | nsISelection* sel = aBuilder->GetBoundingSelection(); |
michael@0 | 5662 | return !sel || IsVisibleInSelection(sel); |
michael@0 | 5663 | } |
michael@0 | 5664 | |
michael@0 | 5665 | bool |
michael@0 | 5666 | nsIFrame::IsVisibleInSelection(nsISelection* aSelection) |
michael@0 | 5667 | { |
michael@0 | 5668 | if (!GetContent() || !GetContent()->IsSelectionDescendant()) { |
michael@0 | 5669 | return false; |
michael@0 | 5670 | } |
michael@0 | 5671 | |
michael@0 | 5672 | nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent)); |
michael@0 | 5673 | bool vis; |
michael@0 | 5674 | nsresult rv = aSelection->ContainsNode(node, true, &vis); |
michael@0 | 5675 | return NS_FAILED(rv) || vis; |
michael@0 | 5676 | } |
michael@0 | 5677 | |
michael@0 | 5678 | /* virtual */ bool |
michael@0 | 5679 | nsFrame::IsEmpty() |
michael@0 | 5680 | { |
michael@0 | 5681 | return false; |
michael@0 | 5682 | } |
michael@0 | 5683 | |
michael@0 | 5684 | bool |
michael@0 | 5685 | nsIFrame::CachedIsEmpty() |
michael@0 | 5686 | { |
michael@0 | 5687 | NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY), |
michael@0 | 5688 | "Must only be called on reflowed lines"); |
michael@0 | 5689 | return IsEmpty(); |
michael@0 | 5690 | } |
michael@0 | 5691 | |
michael@0 | 5692 | /* virtual */ bool |
michael@0 | 5693 | nsFrame::IsSelfEmpty() |
michael@0 | 5694 | { |
michael@0 | 5695 | return false; |
michael@0 | 5696 | } |
michael@0 | 5697 | |
michael@0 | 5698 | nsresult |
michael@0 | 5699 | nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) |
michael@0 | 5700 | { |
michael@0 | 5701 | if (!aPresContext || !aSelCon) |
michael@0 | 5702 | return NS_ERROR_INVALID_ARG; |
michael@0 | 5703 | |
michael@0 | 5704 | nsIFrame *frame = this; |
michael@0 | 5705 | while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) { |
michael@0 | 5706 | nsITextControlFrame *tcf = do_QueryFrame(frame); |
michael@0 | 5707 | if (tcf) { |
michael@0 | 5708 | return tcf->GetOwnedSelectionController(aSelCon); |
michael@0 | 5709 | } |
michael@0 | 5710 | frame = frame->GetParent(); |
michael@0 | 5711 | } |
michael@0 | 5712 | |
michael@0 | 5713 | return CallQueryInterface(aPresContext->GetPresShell(), aSelCon); |
michael@0 | 5714 | } |
michael@0 | 5715 | |
michael@0 | 5716 | already_AddRefed<nsFrameSelection> |
michael@0 | 5717 | nsIFrame::GetFrameSelection() |
michael@0 | 5718 | { |
michael@0 | 5719 | nsRefPtr<nsFrameSelection> fs = |
michael@0 | 5720 | const_cast<nsFrameSelection*>(GetConstFrameSelection()); |
michael@0 | 5721 | return fs.forget(); |
michael@0 | 5722 | } |
michael@0 | 5723 | |
michael@0 | 5724 | const nsFrameSelection* |
michael@0 | 5725 | nsIFrame::GetConstFrameSelection() const |
michael@0 | 5726 | { |
michael@0 | 5727 | nsIFrame* frame = const_cast<nsIFrame*>(this); |
michael@0 | 5728 | while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) { |
michael@0 | 5729 | nsITextControlFrame* tcf = do_QueryFrame(frame); |
michael@0 | 5730 | if (tcf) { |
michael@0 | 5731 | return tcf->GetOwnedFrameSelection(); |
michael@0 | 5732 | } |
michael@0 | 5733 | frame = frame->GetParent(); |
michael@0 | 5734 | } |
michael@0 | 5735 | |
michael@0 | 5736 | return PresContext()->PresShell()->ConstFrameSelection(); |
michael@0 | 5737 | } |
michael@0 | 5738 | |
michael@0 | 5739 | #ifdef DEBUG |
michael@0 | 5740 | nsresult |
michael@0 | 5741 | nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) |
michael@0 | 5742 | { |
michael@0 | 5743 | IndentBy(out, aIndent); |
michael@0 | 5744 | fprintf(out, "<frame va=\"%p\" type=\"", (void*)this); |
michael@0 | 5745 | nsAutoString name; |
michael@0 | 5746 | GetFrameName(name); |
michael@0 | 5747 | XMLQuote(name); |
michael@0 | 5748 | fputs(NS_LossyConvertUTF16toASCII(name).get(), out); |
michael@0 | 5749 | fprintf(out, "\" state=\"%016llx\" parent=\"%p\">\n", |
michael@0 | 5750 | (unsigned long long)GetDebugStateBits(), (void*)mParent); |
michael@0 | 5751 | |
michael@0 | 5752 | aIndent++; |
michael@0 | 5753 | DumpBaseRegressionData(aPresContext, out, aIndent); |
michael@0 | 5754 | aIndent--; |
michael@0 | 5755 | |
michael@0 | 5756 | IndentBy(out, aIndent); |
michael@0 | 5757 | fprintf(out, "</frame>\n"); |
michael@0 | 5758 | |
michael@0 | 5759 | return NS_OK; |
michael@0 | 5760 | } |
michael@0 | 5761 | |
michael@0 | 5762 | void |
michael@0 | 5763 | nsFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) |
michael@0 | 5764 | { |
michael@0 | 5765 | if (GetNextSibling()) { |
michael@0 | 5766 | IndentBy(out, aIndent); |
michael@0 | 5767 | fprintf(out, "<next-sibling va=\"%p\"/>\n", (void*)GetNextSibling()); |
michael@0 | 5768 | } |
michael@0 | 5769 | |
michael@0 | 5770 | if (HasView()) { |
michael@0 | 5771 | IndentBy(out, aIndent); |
michael@0 | 5772 | fprintf(out, "<view va=\"%p\">\n", (void*)GetView()); |
michael@0 | 5773 | aIndent++; |
michael@0 | 5774 | // XXX add in code to dump out view state too... |
michael@0 | 5775 | aIndent--; |
michael@0 | 5776 | IndentBy(out, aIndent); |
michael@0 | 5777 | fprintf(out, "</view>\n"); |
michael@0 | 5778 | } |
michael@0 | 5779 | |
michael@0 | 5780 | IndentBy(out, aIndent); |
michael@0 | 5781 | fprintf(out, "<bbox x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\"/>\n", |
michael@0 | 5782 | mRect.x, mRect.y, mRect.width, mRect.height); |
michael@0 | 5783 | |
michael@0 | 5784 | // Now dump all of the children on all of the child lists |
michael@0 | 5785 | ChildListIterator lists(this); |
michael@0 | 5786 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 5787 | IndentBy(out, aIndent); |
michael@0 | 5788 | if (lists.CurrentID() != kPrincipalList) { |
michael@0 | 5789 | fprintf(out, "<child-list name=\"%s\">\n", mozilla::layout::ChildListName(lists.CurrentID())); |
michael@0 | 5790 | } |
michael@0 | 5791 | else { |
michael@0 | 5792 | fprintf(out, "<child-list>\n"); |
michael@0 | 5793 | } |
michael@0 | 5794 | aIndent++; |
michael@0 | 5795 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 5796 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 5797 | nsIFrame* kid = childFrames.get(); |
michael@0 | 5798 | kid->DumpRegressionData(aPresContext, out, aIndent); |
michael@0 | 5799 | } |
michael@0 | 5800 | aIndent--; |
michael@0 | 5801 | IndentBy(out, aIndent); |
michael@0 | 5802 | fprintf(out, "</child-list>\n"); |
michael@0 | 5803 | } |
michael@0 | 5804 | } |
michael@0 | 5805 | #endif |
michael@0 | 5806 | |
michael@0 | 5807 | bool |
michael@0 | 5808 | nsIFrame::IsFrameSelected() const |
michael@0 | 5809 | { |
michael@0 | 5810 | NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(), |
michael@0 | 5811 | "use the public IsSelected() instead"); |
michael@0 | 5812 | return nsRange::IsNodeSelected(GetContent(), 0, |
michael@0 | 5813 | GetContent()->GetChildCount()); |
michael@0 | 5814 | } |
michael@0 | 5815 | |
michael@0 | 5816 | nsresult |
michael@0 | 5817 | nsFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) |
michael@0 | 5818 | { |
michael@0 | 5819 | NS_PRECONDITION(outPoint != nullptr, "Null parameter"); |
michael@0 | 5820 | nsRect contentRect = GetContentRect() - GetPosition(); |
michael@0 | 5821 | nsPoint pt = contentRect.TopLeft(); |
michael@0 | 5822 | if (mContent) |
michael@0 | 5823 | { |
michael@0 | 5824 | nsIContent* newContent = mContent->GetParent(); |
michael@0 | 5825 | if (newContent){ |
michael@0 | 5826 | int32_t newOffset = newContent->IndexOf(mContent); |
michael@0 | 5827 | |
michael@0 | 5828 | bool isRTL = (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1; |
michael@0 | 5829 | if ((!isRTL && inOffset > newOffset) || |
michael@0 | 5830 | (isRTL && inOffset <= newOffset)) { |
michael@0 | 5831 | pt = contentRect.TopRight(); |
michael@0 | 5832 | } |
michael@0 | 5833 | } |
michael@0 | 5834 | } |
michael@0 | 5835 | *outPoint = pt; |
michael@0 | 5836 | return NS_OK; |
michael@0 | 5837 | } |
michael@0 | 5838 | |
michael@0 | 5839 | nsresult |
michael@0 | 5840 | nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, int32_t* outFrameContentOffset, nsIFrame **outChildFrame) |
michael@0 | 5841 | { |
michael@0 | 5842 | NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter"); |
michael@0 | 5843 | *outFrameContentOffset = (int32_t)inHint; |
michael@0 | 5844 | //the best frame to reflect any given offset would be a visible frame if possible |
michael@0 | 5845 | //i.e. we are looking for a valid frame to place the blinking caret |
michael@0 | 5846 | nsRect rect = GetRect(); |
michael@0 | 5847 | if (!rect.width || !rect.height) |
michael@0 | 5848 | { |
michael@0 | 5849 | //if we have a 0 width or height then lets look for another frame that possibly has |
michael@0 | 5850 | //the same content. If we have no frames in flow then just let us return 'this' frame |
michael@0 | 5851 | nsIFrame* nextFlow = GetNextInFlow(); |
michael@0 | 5852 | if (nextFlow) |
michael@0 | 5853 | return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame); |
michael@0 | 5854 | } |
michael@0 | 5855 | *outChildFrame = this; |
michael@0 | 5856 | return NS_OK; |
michael@0 | 5857 | } |
michael@0 | 5858 | |
michael@0 | 5859 | // |
michael@0 | 5860 | // What I've pieced together about this routine: |
michael@0 | 5861 | // Starting with a block frame (from which a line frame can be gotten) |
michael@0 | 5862 | // and a line number, drill down and get the first/last selectable |
michael@0 | 5863 | // frame on that line, depending on aPos->mDirection. |
michael@0 | 5864 | // aOutSideLimit != 0 means ignore aLineStart, instead work from |
michael@0 | 5865 | // the end (if > 0) or beginning (if < 0). |
michael@0 | 5866 | // |
michael@0 | 5867 | nsresult |
michael@0 | 5868 | nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext, |
michael@0 | 5869 | nsPeekOffsetStruct *aPos, |
michael@0 | 5870 | nsIFrame *aBlockFrame, |
michael@0 | 5871 | int32_t aLineStart, |
michael@0 | 5872 | int8_t aOutSideLimit |
michael@0 | 5873 | ) |
michael@0 | 5874 | { |
michael@0 | 5875 | //magic numbers aLineStart will be -1 for end of block 0 will be start of block |
michael@0 | 5876 | if (!aBlockFrame || !aPos) |
michael@0 | 5877 | return NS_ERROR_NULL_POINTER; |
michael@0 | 5878 | |
michael@0 | 5879 | aPos->mResultFrame = nullptr; |
michael@0 | 5880 | aPos->mResultContent = nullptr; |
michael@0 | 5881 | aPos->mAttachForward = (aPos->mDirection == eDirNext); |
michael@0 | 5882 | |
michael@0 | 5883 | nsAutoLineIterator it = aBlockFrame->GetLineIterator(); |
michael@0 | 5884 | if (!it) |
michael@0 | 5885 | return NS_ERROR_FAILURE; |
michael@0 | 5886 | int32_t searchingLine = aLineStart; |
michael@0 | 5887 | int32_t countLines = it->GetNumLines(); |
michael@0 | 5888 | if (aOutSideLimit > 0) //start at end |
michael@0 | 5889 | searchingLine = countLines; |
michael@0 | 5890 | else if (aOutSideLimit <0)//start at beginning |
michael@0 | 5891 | searchingLine = -1;//"next" will be 0 |
michael@0 | 5892 | else |
michael@0 | 5893 | if ((aPos->mDirection == eDirPrevious && searchingLine == 0) || |
michael@0 | 5894 | (aPos->mDirection == eDirNext && searchingLine >= (countLines -1) )){ |
michael@0 | 5895 | //we need to jump to new block frame. |
michael@0 | 5896 | return NS_ERROR_FAILURE; |
michael@0 | 5897 | } |
michael@0 | 5898 | int32_t lineFrameCount; |
michael@0 | 5899 | nsIFrame *resultFrame = nullptr; |
michael@0 | 5900 | nsIFrame *farStoppingFrame = nullptr; //we keep searching until we find a "this" frame then we go to next line |
michael@0 | 5901 | nsIFrame *nearStoppingFrame = nullptr; //if we are backing up from edge, stop here |
michael@0 | 5902 | nsIFrame *firstFrame; |
michael@0 | 5903 | nsIFrame *lastFrame; |
michael@0 | 5904 | nsRect rect; |
michael@0 | 5905 | bool isBeforeFirstFrame, isAfterLastFrame; |
michael@0 | 5906 | bool found = false; |
michael@0 | 5907 | |
michael@0 | 5908 | nsresult result = NS_OK; |
michael@0 | 5909 | while (!found) |
michael@0 | 5910 | { |
michael@0 | 5911 | if (aPos->mDirection == eDirPrevious) |
michael@0 | 5912 | searchingLine --; |
michael@0 | 5913 | else |
michael@0 | 5914 | searchingLine ++; |
michael@0 | 5915 | if ((aPos->mDirection == eDirPrevious && searchingLine < 0) || |
michael@0 | 5916 | (aPos->mDirection == eDirNext && searchingLine >= countLines )) |
michael@0 | 5917 | { |
michael@0 | 5918 | //we need to jump to new block frame. |
michael@0 | 5919 | return NS_ERROR_FAILURE; |
michael@0 | 5920 | } |
michael@0 | 5921 | uint32_t lineFlags; |
michael@0 | 5922 | result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount, |
michael@0 | 5923 | rect, &lineFlags); |
michael@0 | 5924 | if (!lineFrameCount) |
michael@0 | 5925 | continue; |
michael@0 | 5926 | if (NS_SUCCEEDED(result)){ |
michael@0 | 5927 | lastFrame = firstFrame; |
michael@0 | 5928 | for (;lineFrameCount > 1;lineFrameCount --){ |
michael@0 | 5929 | //result = lastFrame->GetNextSibling(&lastFrame, searchingLine); |
michael@0 | 5930 | result = it->GetNextSiblingOnLine(lastFrame, searchingLine); |
michael@0 | 5931 | if (NS_FAILED(result) || !lastFrame){ |
michael@0 | 5932 | NS_ERROR("GetLine promised more frames than could be found"); |
michael@0 | 5933 | return NS_ERROR_FAILURE; |
michael@0 | 5934 | } |
michael@0 | 5935 | } |
michael@0 | 5936 | GetLastLeaf(aPresContext, &lastFrame); |
michael@0 | 5937 | |
michael@0 | 5938 | if (aPos->mDirection == eDirNext){ |
michael@0 | 5939 | nearStoppingFrame = firstFrame; |
michael@0 | 5940 | farStoppingFrame = lastFrame; |
michael@0 | 5941 | } |
michael@0 | 5942 | else{ |
michael@0 | 5943 | nearStoppingFrame = lastFrame; |
michael@0 | 5944 | farStoppingFrame = firstFrame; |
michael@0 | 5945 | } |
michael@0 | 5946 | nsPoint offset; |
michael@0 | 5947 | nsView * view; //used for call of get offset from view |
michael@0 | 5948 | aBlockFrame->GetOffsetFromView(offset,&view); |
michael@0 | 5949 | nscoord newDesiredX = aPos->mDesiredX - offset.x;//get desired x into blockframe coordinates! |
michael@0 | 5950 | result = it->FindFrameAt(searchingLine, newDesiredX, &resultFrame, &isBeforeFirstFrame, &isAfterLastFrame); |
michael@0 | 5951 | if(NS_FAILED(result)) |
michael@0 | 5952 | continue; |
michael@0 | 5953 | } |
michael@0 | 5954 | |
michael@0 | 5955 | if (NS_SUCCEEDED(result) && resultFrame) |
michael@0 | 5956 | { |
michael@0 | 5957 | //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines |
michael@0 | 5958 | nsAutoLineIterator newIt = resultFrame->GetLineIterator(); |
michael@0 | 5959 | if (newIt) |
michael@0 | 5960 | { |
michael@0 | 5961 | aPos->mResultFrame = resultFrame; |
michael@0 | 5962 | return NS_OK; |
michael@0 | 5963 | } |
michael@0 | 5964 | //resultFrame is not a block frame |
michael@0 | 5965 | result = NS_ERROR_FAILURE; |
michael@0 | 5966 | |
michael@0 | 5967 | nsCOMPtr<nsIFrameEnumerator> frameTraversal; |
michael@0 | 5968 | result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), |
michael@0 | 5969 | aPresContext, resultFrame, |
michael@0 | 5970 | ePostOrder, |
michael@0 | 5971 | false, // aVisual |
michael@0 | 5972 | aPos->mScrollViewStop, |
michael@0 | 5973 | false // aFollowOOFs |
michael@0 | 5974 | ); |
michael@0 | 5975 | if (NS_FAILED(result)) |
michael@0 | 5976 | return result; |
michael@0 | 5977 | |
michael@0 | 5978 | nsIFrame *storeOldResultFrame = resultFrame; |
michael@0 | 5979 | while ( !found ){ |
michael@0 | 5980 | nsPoint point; |
michael@0 | 5981 | point.x = aPos->mDesiredX; |
michael@0 | 5982 | |
michael@0 | 5983 | nsRect tempRect = resultFrame->GetRect(); |
michael@0 | 5984 | nsPoint offset; |
michael@0 | 5985 | nsView * view; //used for call of get offset from view |
michael@0 | 5986 | resultFrame->GetOffsetFromView(offset, &view); |
michael@0 | 5987 | if (!view) { |
michael@0 | 5988 | return NS_ERROR_FAILURE; |
michael@0 | 5989 | } |
michael@0 | 5990 | point.y = tempRect.height + offset.y; |
michael@0 | 5991 | |
michael@0 | 5992 | //special check. if we allow non-text selection then we can allow a hit location to fall before a table. |
michael@0 | 5993 | //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself) |
michael@0 | 5994 | nsIPresShell *shell = aPresContext->GetPresShell(); |
michael@0 | 5995 | if (!shell) |
michael@0 | 5996 | return NS_ERROR_FAILURE; |
michael@0 | 5997 | int16_t isEditor = shell->GetSelectionFlags(); |
michael@0 | 5998 | isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL; |
michael@0 | 5999 | if ( isEditor ) |
michael@0 | 6000 | { |
michael@0 | 6001 | if (resultFrame->GetType() == nsGkAtoms::tableOuterFrame) |
michael@0 | 6002 | { |
michael@0 | 6003 | if (((point.x - offset.x + tempRect.x)<0) || ((point.x - offset.x+ tempRect.x)>tempRect.width))//off left/right side |
michael@0 | 6004 | { |
michael@0 | 6005 | nsIContent* content = resultFrame->GetContent(); |
michael@0 | 6006 | if (content) |
michael@0 | 6007 | { |
michael@0 | 6008 | nsIContent* parent = content->GetParent(); |
michael@0 | 6009 | if (parent) |
michael@0 | 6010 | { |
michael@0 | 6011 | aPos->mResultContent = parent; |
michael@0 | 6012 | aPos->mContentOffset = parent->IndexOf(content); |
michael@0 | 6013 | aPos->mAttachForward = false; |
michael@0 | 6014 | if ((point.x - offset.x+ tempRect.x)>tempRect.width) |
michael@0 | 6015 | { |
michael@0 | 6016 | aPos->mContentOffset++;//go to end of this frame |
michael@0 | 6017 | aPos->mAttachForward = true; |
michael@0 | 6018 | } |
michael@0 | 6019 | //result frame is the result frames parent. |
michael@0 | 6020 | aPos->mResultFrame = resultFrame->GetParent(); |
michael@0 | 6021 | return NS_POSITION_BEFORE_TABLE; |
michael@0 | 6022 | } |
michael@0 | 6023 | } |
michael@0 | 6024 | } |
michael@0 | 6025 | } |
michael@0 | 6026 | } |
michael@0 | 6027 | |
michael@0 | 6028 | if (!resultFrame->HasView()) |
michael@0 | 6029 | { |
michael@0 | 6030 | nsView* view; |
michael@0 | 6031 | nsPoint offset; |
michael@0 | 6032 | resultFrame->GetOffsetFromView(offset, &view); |
michael@0 | 6033 | ContentOffsets offsets = |
michael@0 | 6034 | resultFrame->GetContentOffsetsFromPoint(point - offset); |
michael@0 | 6035 | aPos->mResultContent = offsets.content; |
michael@0 | 6036 | aPos->mContentOffset = offsets.offset; |
michael@0 | 6037 | aPos->mAttachForward = offsets.associateWithNext; |
michael@0 | 6038 | if (offsets.content) |
michael@0 | 6039 | { |
michael@0 | 6040 | bool selectable; |
michael@0 | 6041 | resultFrame->IsSelectable(&selectable, nullptr); |
michael@0 | 6042 | if (selectable) |
michael@0 | 6043 | { |
michael@0 | 6044 | found = true; |
michael@0 | 6045 | break; |
michael@0 | 6046 | } |
michael@0 | 6047 | } |
michael@0 | 6048 | } |
michael@0 | 6049 | |
michael@0 | 6050 | if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame)) |
michael@0 | 6051 | break; |
michael@0 | 6052 | if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame)) |
michael@0 | 6053 | break; |
michael@0 | 6054 | //always try previous on THAT line if that fails go the other way |
michael@0 | 6055 | frameTraversal->Prev(); |
michael@0 | 6056 | resultFrame = frameTraversal->CurrentItem(); |
michael@0 | 6057 | if (!resultFrame) |
michael@0 | 6058 | return NS_ERROR_FAILURE; |
michael@0 | 6059 | } |
michael@0 | 6060 | |
michael@0 | 6061 | if (!found){ |
michael@0 | 6062 | resultFrame = storeOldResultFrame; |
michael@0 | 6063 | |
michael@0 | 6064 | result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), |
michael@0 | 6065 | aPresContext, resultFrame, |
michael@0 | 6066 | eLeaf, |
michael@0 | 6067 | false, // aVisual |
michael@0 | 6068 | aPos->mScrollViewStop, |
michael@0 | 6069 | false // aFollowOOFs |
michael@0 | 6070 | ); |
michael@0 | 6071 | } |
michael@0 | 6072 | while ( !found ){ |
michael@0 | 6073 | nsPoint point(aPos->mDesiredX, 0); |
michael@0 | 6074 | nsView* view; |
michael@0 | 6075 | nsPoint offset; |
michael@0 | 6076 | resultFrame->GetOffsetFromView(offset, &view); |
michael@0 | 6077 | ContentOffsets offsets = |
michael@0 | 6078 | resultFrame->GetContentOffsetsFromPoint(point - offset); |
michael@0 | 6079 | aPos->mResultContent = offsets.content; |
michael@0 | 6080 | aPos->mContentOffset = offsets.offset; |
michael@0 | 6081 | aPos->mAttachForward = offsets.associateWithNext; |
michael@0 | 6082 | if (offsets.content) |
michael@0 | 6083 | { |
michael@0 | 6084 | bool selectable; |
michael@0 | 6085 | resultFrame->IsSelectable(&selectable, nullptr); |
michael@0 | 6086 | if (selectable) |
michael@0 | 6087 | { |
michael@0 | 6088 | found = true; |
michael@0 | 6089 | if (resultFrame == farStoppingFrame) |
michael@0 | 6090 | aPos->mAttachForward = false; |
michael@0 | 6091 | else |
michael@0 | 6092 | aPos->mAttachForward = true; |
michael@0 | 6093 | break; |
michael@0 | 6094 | } |
michael@0 | 6095 | } |
michael@0 | 6096 | if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame)) |
michael@0 | 6097 | break; |
michael@0 | 6098 | if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame)) |
michael@0 | 6099 | break; |
michael@0 | 6100 | //previous didnt work now we try "next" |
michael@0 | 6101 | frameTraversal->Next(); |
michael@0 | 6102 | nsIFrame *tempFrame = frameTraversal->CurrentItem(); |
michael@0 | 6103 | if (!tempFrame) |
michael@0 | 6104 | break; |
michael@0 | 6105 | resultFrame = tempFrame; |
michael@0 | 6106 | } |
michael@0 | 6107 | aPos->mResultFrame = resultFrame; |
michael@0 | 6108 | } |
michael@0 | 6109 | else { |
michael@0 | 6110 | //we need to jump to new block frame. |
michael@0 | 6111 | aPos->mAmount = eSelectLine; |
michael@0 | 6112 | aPos->mStartOffset = 0; |
michael@0 | 6113 | aPos->mAttachForward = !(aPos->mDirection == eDirNext); |
michael@0 | 6114 | if (aPos->mDirection == eDirPrevious) |
michael@0 | 6115 | aPos->mStartOffset = -1;//start from end |
michael@0 | 6116 | return aBlockFrame->PeekOffset(aPos); |
michael@0 | 6117 | } |
michael@0 | 6118 | } |
michael@0 | 6119 | return NS_OK; |
michael@0 | 6120 | } |
michael@0 | 6121 | |
michael@0 | 6122 | nsIFrame::CaretPosition |
michael@0 | 6123 | nsIFrame::GetExtremeCaretPosition(bool aStart) |
michael@0 | 6124 | { |
michael@0 | 6125 | CaretPosition result; |
michael@0 | 6126 | |
michael@0 | 6127 | FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0); |
michael@0 | 6128 | FrameContentRange range = GetRangeForFrame(targetFrame.frame); |
michael@0 | 6129 | result.mResultContent = range.content; |
michael@0 | 6130 | result.mContentOffset = aStart ? range.start : range.end; |
michael@0 | 6131 | return result; |
michael@0 | 6132 | } |
michael@0 | 6133 | |
michael@0 | 6134 | // Find the first (or last) descendant of the given frame |
michael@0 | 6135 | // which is either a block frame or a BRFrame. |
michael@0 | 6136 | static nsContentAndOffset |
michael@0 | 6137 | FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection) |
michael@0 | 6138 | { |
michael@0 | 6139 | nsContentAndOffset result; |
michael@0 | 6140 | result.mContent = nullptr; |
michael@0 | 6141 | result.mOffset = 0; |
michael@0 | 6142 | |
michael@0 | 6143 | if (aFrame->IsGeneratedContentFrame()) |
michael@0 | 6144 | return result; |
michael@0 | 6145 | |
michael@0 | 6146 | // Treat form controls as inline leaves |
michael@0 | 6147 | // XXX we really need a way to determine whether a frame is inline-level |
michael@0 | 6148 | nsIFormControlFrame* fcf = do_QueryFrame(aFrame); |
michael@0 | 6149 | if (fcf) |
michael@0 | 6150 | return result; |
michael@0 | 6151 | |
michael@0 | 6152 | // Check the frame itself |
michael@0 | 6153 | // Fall through block-in-inline split frames because their mContent is |
michael@0 | 6154 | // the content of the inline frames they were created from. The |
michael@0 | 6155 | // first/last child of such frames is the real block frame we're |
michael@0 | 6156 | // looking for. |
michael@0 | 6157 | if ((nsLayoutUtils::GetAsBlock(aFrame) && |
michael@0 | 6158 | !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) || |
michael@0 | 6159 | aFrame->GetType() == nsGkAtoms::brFrame) { |
michael@0 | 6160 | nsIContent* content = aFrame->GetContent(); |
michael@0 | 6161 | result.mContent = content->GetParent(); |
michael@0 | 6162 | // In some cases (bug 310589, bug 370174) we end up here with a null content. |
michael@0 | 6163 | // This probably shouldn't ever happen, but since it sometimes does, we want |
michael@0 | 6164 | // to avoid crashing here. |
michael@0 | 6165 | NS_ASSERTION(result.mContent, "Unexpected orphan content"); |
michael@0 | 6166 | if (result.mContent) |
michael@0 | 6167 | result.mOffset = result.mContent->IndexOf(content) + |
michael@0 | 6168 | (aDirection == eDirPrevious ? 1 : 0); |
michael@0 | 6169 | return result; |
michael@0 | 6170 | } |
michael@0 | 6171 | |
michael@0 | 6172 | // If this is a preformatted text frame, see if it ends with a newline |
michael@0 | 6173 | if (aFrame->HasSignificantTerminalNewline()) { |
michael@0 | 6174 | int32_t startOffset, endOffset; |
michael@0 | 6175 | aFrame->GetOffsets(startOffset, endOffset); |
michael@0 | 6176 | result.mContent = aFrame->GetContent(); |
michael@0 | 6177 | result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1); |
michael@0 | 6178 | return result; |
michael@0 | 6179 | } |
michael@0 | 6180 | |
michael@0 | 6181 | // Iterate over children and call ourselves recursively |
michael@0 | 6182 | if (aDirection == eDirPrevious) { |
michael@0 | 6183 | nsIFrame* child = aFrame->GetLastChild(nsIFrame::kPrincipalList); |
michael@0 | 6184 | while(child && !result.mContent) { |
michael@0 | 6185 | result = FindBlockFrameOrBR(child, aDirection); |
michael@0 | 6186 | child = child->GetPrevSibling(); |
michael@0 | 6187 | } |
michael@0 | 6188 | } else { // eDirNext |
michael@0 | 6189 | nsIFrame* child = aFrame->GetFirstPrincipalChild(); |
michael@0 | 6190 | while(child && !result.mContent) { |
michael@0 | 6191 | result = FindBlockFrameOrBR(child, aDirection); |
michael@0 | 6192 | child = child->GetNextSibling(); |
michael@0 | 6193 | } |
michael@0 | 6194 | } |
michael@0 | 6195 | return result; |
michael@0 | 6196 | } |
michael@0 | 6197 | |
michael@0 | 6198 | nsresult |
michael@0 | 6199 | nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos) |
michael@0 | 6200 | { |
michael@0 | 6201 | nsIFrame* frame = this; |
michael@0 | 6202 | nsContentAndOffset blockFrameOrBR; |
michael@0 | 6203 | blockFrameOrBR.mContent = nullptr; |
michael@0 | 6204 | bool reachedBlockAncestor = false; |
michael@0 | 6205 | |
michael@0 | 6206 | // Go through containing frames until reaching a block frame. |
michael@0 | 6207 | // In each step, search the previous (or next) siblings for the closest |
michael@0 | 6208 | // "stop frame" (a block frame or a BRFrame). |
michael@0 | 6209 | // If found, set it to be the selection boundray and abort. |
michael@0 | 6210 | |
michael@0 | 6211 | if (aPos->mDirection == eDirPrevious) { |
michael@0 | 6212 | while (!reachedBlockAncestor) { |
michael@0 | 6213 | nsIFrame* parent = frame->GetParent(); |
michael@0 | 6214 | // Treat a frame associated with the root content as if it were a block frame. |
michael@0 | 6215 | if (!frame->mContent || !frame->mContent->GetParent()) { |
michael@0 | 6216 | reachedBlockAncestor = true; |
michael@0 | 6217 | break; |
michael@0 | 6218 | } |
michael@0 | 6219 | nsIFrame* sibling = frame->GetPrevSibling(); |
michael@0 | 6220 | while (sibling && !blockFrameOrBR.mContent) { |
michael@0 | 6221 | blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirPrevious); |
michael@0 | 6222 | sibling = sibling->GetPrevSibling(); |
michael@0 | 6223 | } |
michael@0 | 6224 | if (blockFrameOrBR.mContent) { |
michael@0 | 6225 | aPos->mResultContent = blockFrameOrBR.mContent; |
michael@0 | 6226 | aPos->mContentOffset = blockFrameOrBR.mOffset; |
michael@0 | 6227 | break; |
michael@0 | 6228 | } |
michael@0 | 6229 | frame = parent; |
michael@0 | 6230 | reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr); |
michael@0 | 6231 | } |
michael@0 | 6232 | if (reachedBlockAncestor) { // no "stop frame" found |
michael@0 | 6233 | aPos->mResultContent = frame->GetContent(); |
michael@0 | 6234 | aPos->mContentOffset = 0; |
michael@0 | 6235 | } |
michael@0 | 6236 | } else { // eDirNext |
michael@0 | 6237 | while (!reachedBlockAncestor) { |
michael@0 | 6238 | nsIFrame* parent = frame->GetParent(); |
michael@0 | 6239 | // Treat a frame associated with the root content as if it were a block frame. |
michael@0 | 6240 | if (!frame->mContent || !frame->mContent->GetParent()) { |
michael@0 | 6241 | reachedBlockAncestor = true; |
michael@0 | 6242 | break; |
michael@0 | 6243 | } |
michael@0 | 6244 | nsIFrame* sibling = frame; |
michael@0 | 6245 | while (sibling && !blockFrameOrBR.mContent) { |
michael@0 | 6246 | blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirNext); |
michael@0 | 6247 | sibling = sibling->GetNextSibling(); |
michael@0 | 6248 | } |
michael@0 | 6249 | if (blockFrameOrBR.mContent) { |
michael@0 | 6250 | aPos->mResultContent = blockFrameOrBR.mContent; |
michael@0 | 6251 | aPos->mContentOffset = blockFrameOrBR.mOffset; |
michael@0 | 6252 | break; |
michael@0 | 6253 | } |
michael@0 | 6254 | frame = parent; |
michael@0 | 6255 | reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr); |
michael@0 | 6256 | } |
michael@0 | 6257 | if (reachedBlockAncestor) { // no "stop frame" found |
michael@0 | 6258 | aPos->mResultContent = frame->GetContent(); |
michael@0 | 6259 | if (aPos->mResultContent) |
michael@0 | 6260 | aPos->mContentOffset = aPos->mResultContent->GetChildCount(); |
michael@0 | 6261 | } |
michael@0 | 6262 | } |
michael@0 | 6263 | return NS_OK; |
michael@0 | 6264 | } |
michael@0 | 6265 | |
michael@0 | 6266 | // Determine movement direction relative to frame |
michael@0 | 6267 | static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, bool aVisual) |
michael@0 | 6268 | { |
michael@0 | 6269 | bool isReverseDirection = aVisual ? |
michael@0 | 6270 | (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : false; |
michael@0 | 6271 | return aDirection == (isReverseDirection ? eDirPrevious : eDirNext); |
michael@0 | 6272 | } |
michael@0 | 6273 | |
michael@0 | 6274 | nsresult |
michael@0 | 6275 | nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) |
michael@0 | 6276 | { |
michael@0 | 6277 | if (!aPos) |
michael@0 | 6278 | return NS_ERROR_NULL_POINTER; |
michael@0 | 6279 | nsresult result = NS_ERROR_FAILURE; |
michael@0 | 6280 | |
michael@0 | 6281 | if (mState & NS_FRAME_IS_DIRTY) |
michael@0 | 6282 | return NS_ERROR_UNEXPECTED; |
michael@0 | 6283 | |
michael@0 | 6284 | // Translate content offset to be relative to frame |
michael@0 | 6285 | FrameContentRange range = GetRangeForFrame(this); |
michael@0 | 6286 | int32_t offset = aPos->mStartOffset - range.start; |
michael@0 | 6287 | nsIFrame* current = this; |
michael@0 | 6288 | |
michael@0 | 6289 | switch (aPos->mAmount) { |
michael@0 | 6290 | case eSelectCharacter: |
michael@0 | 6291 | case eSelectCluster: |
michael@0 | 6292 | { |
michael@0 | 6293 | bool eatingNonRenderableWS = false; |
michael@0 | 6294 | nsIFrame::FrameSearchResult peekSearchState = CONTINUE; |
michael@0 | 6295 | bool jumpedLine = false; |
michael@0 | 6296 | bool movedOverNonSelectableText = false; |
michael@0 | 6297 | |
michael@0 | 6298 | while (peekSearchState != FOUND) { |
michael@0 | 6299 | bool movingInFrameDirection = |
michael@0 | 6300 | IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); |
michael@0 | 6301 | |
michael@0 | 6302 | if (eatingNonRenderableWS) |
michael@0 | 6303 | peekSearchState = current->PeekOffsetNoAmount(movingInFrameDirection, &offset); |
michael@0 | 6304 | else |
michael@0 | 6305 | peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection, &offset, |
michael@0 | 6306 | aPos->mAmount == eSelectCluster); |
michael@0 | 6307 | |
michael@0 | 6308 | movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE); |
michael@0 | 6309 | |
michael@0 | 6310 | if (peekSearchState != FOUND) { |
michael@0 | 6311 | result = |
michael@0 | 6312 | current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, |
michael@0 | 6313 | aPos->mJumpLines, aPos->mScrollViewStop, |
michael@0 | 6314 | ¤t, &offset, &jumpedLine); |
michael@0 | 6315 | if (NS_FAILED(result)) |
michael@0 | 6316 | return result; |
michael@0 | 6317 | |
michael@0 | 6318 | // If we jumped lines, it's as if we found a character, but we still need |
michael@0 | 6319 | // to eat non-renderable content on the new line. |
michael@0 | 6320 | if (jumpedLine) |
michael@0 | 6321 | eatingNonRenderableWS = true; |
michael@0 | 6322 | } |
michael@0 | 6323 | |
michael@0 | 6324 | // Found frame, but because we moved over non selectable text we want the offset |
michael@0 | 6325 | // to be at the frame edge. |
michael@0 | 6326 | if (peekSearchState == FOUND && movedOverNonSelectableText) |
michael@0 | 6327 | { |
michael@0 | 6328 | int32_t start, end; |
michael@0 | 6329 | current->GetOffsets(start, end); |
michael@0 | 6330 | offset = aPos->mDirection == eDirNext ? 0 : end - start; |
michael@0 | 6331 | } |
michael@0 | 6332 | } |
michael@0 | 6333 | |
michael@0 | 6334 | // Set outputs |
michael@0 | 6335 | range = GetRangeForFrame(current); |
michael@0 | 6336 | aPos->mResultFrame = current; |
michael@0 | 6337 | aPos->mResultContent = range.content; |
michael@0 | 6338 | // Output offset is relative to content, not frame |
michael@0 | 6339 | aPos->mContentOffset = offset < 0 ? range.end : range.start + offset; |
michael@0 | 6340 | // If we're dealing with a text frame and moving backward positions us at |
michael@0 | 6341 | // the end of that line, decrease the offset by one to make sure that |
michael@0 | 6342 | // we're placed before the linefeed character on the previous line. |
michael@0 | 6343 | if (offset < 0 && jumpedLine && |
michael@0 | 6344 | aPos->mDirection == eDirPrevious && |
michael@0 | 6345 | current->HasSignificantTerminalNewline()) { |
michael@0 | 6346 | --aPos->mContentOffset; |
michael@0 | 6347 | } |
michael@0 | 6348 | |
michael@0 | 6349 | break; |
michael@0 | 6350 | } |
michael@0 | 6351 | case eSelectWordNoSpace: |
michael@0 | 6352 | // eSelectWordNoSpace means that we should not be eating any whitespace when |
michael@0 | 6353 | // moving to the adjacent word. This means that we should set aPos-> |
michael@0 | 6354 | // mWordMovementType to eEndWord if we're moving forwards, and to eStartWord |
michael@0 | 6355 | // if we're moving backwards. |
michael@0 | 6356 | if (aPos->mDirection == eDirPrevious) { |
michael@0 | 6357 | aPos->mWordMovementType = eStartWord; |
michael@0 | 6358 | } else { |
michael@0 | 6359 | aPos->mWordMovementType = eEndWord; |
michael@0 | 6360 | } |
michael@0 | 6361 | // Intentionally fall through the eSelectWord case. |
michael@0 | 6362 | case eSelectWord: |
michael@0 | 6363 | { |
michael@0 | 6364 | // wordSelectEatSpace means "are we looking for a boundary between whitespace |
michael@0 | 6365 | // and non-whitespace (in the direction we're moving in)". |
michael@0 | 6366 | // It is true when moving forward and looking for a beginning of a word, or |
michael@0 | 6367 | // when moving backwards and looking for an end of a word. |
michael@0 | 6368 | bool wordSelectEatSpace; |
michael@0 | 6369 | if (aPos->mWordMovementType != eDefaultBehavior) { |
michael@0 | 6370 | // aPos->mWordMovementType possible values: |
michael@0 | 6371 | // eEndWord: eat the space if we're moving backwards |
michael@0 | 6372 | // eStartWord: eat the space if we're moving forwards |
michael@0 | 6373 | wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious)); |
michael@0 | 6374 | } |
michael@0 | 6375 | else { |
michael@0 | 6376 | // Use the hidden preference which is based on operating system behavior. |
michael@0 | 6377 | // This pref only affects whether moving forward by word should go to the end of this word or start of the next word. |
michael@0 | 6378 | // When going backwards, the start of the word is always used, on every operating system. |
michael@0 | 6379 | wordSelectEatSpace = aPos->mDirection == eDirNext && |
michael@0 | 6380 | Preferences::GetBool("layout.word_select.eat_space_to_next_word"); |
michael@0 | 6381 | } |
michael@0 | 6382 | |
michael@0 | 6383 | // mSawBeforeType means "we already saw characters of the type |
michael@0 | 6384 | // before the boundary we're looking for". Examples: |
michael@0 | 6385 | // 1. If we're moving forward, looking for a word beginning (i.e. a boundary |
michael@0 | 6386 | // between whitespace and non-whitespace), then eatingWS==true means |
michael@0 | 6387 | // "we already saw some whitespace". |
michael@0 | 6388 | // 2. If we're moving backward, looking for a word beginning (i.e. a boundary |
michael@0 | 6389 | // between non-whitespace and whitespace), then eatingWS==true means |
michael@0 | 6390 | // "we already saw some non-whitespace". |
michael@0 | 6391 | PeekWordState state; |
michael@0 | 6392 | int32_t offsetAdjustment = 0; |
michael@0 | 6393 | bool done = false; |
michael@0 | 6394 | while (!done) { |
michael@0 | 6395 | bool movingInFrameDirection = |
michael@0 | 6396 | IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); |
michael@0 | 6397 | |
michael@0 | 6398 | done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace, |
michael@0 | 6399 | aPos->mIsKeyboardSelect, &offset, &state) == FOUND; |
michael@0 | 6400 | |
michael@0 | 6401 | if (!done) { |
michael@0 | 6402 | nsIFrame* nextFrame; |
michael@0 | 6403 | int32_t nextFrameOffset; |
michael@0 | 6404 | bool jumpedLine; |
michael@0 | 6405 | result = |
michael@0 | 6406 | current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, |
michael@0 | 6407 | aPos->mJumpLines, aPos->mScrollViewStop, |
michael@0 | 6408 | &nextFrame, &nextFrameOffset, &jumpedLine); |
michael@0 | 6409 | // We can't jump lines if we're looking for whitespace following |
michael@0 | 6410 | // non-whitespace, and we already encountered non-whitespace. |
michael@0 | 6411 | if (NS_FAILED(result) || |
michael@0 | 6412 | (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) { |
michael@0 | 6413 | done = true; |
michael@0 | 6414 | // If we've crossed the line boundary, check to make sure that we |
michael@0 | 6415 | // have not consumed a trailing newline as whitesapce if it's significant. |
michael@0 | 6416 | if (jumpedLine && wordSelectEatSpace && |
michael@0 | 6417 | current->HasSignificantTerminalNewline()) { |
michael@0 | 6418 | offsetAdjustment = -1; |
michael@0 | 6419 | } |
michael@0 | 6420 | } else { |
michael@0 | 6421 | if (jumpedLine) { |
michael@0 | 6422 | state.mContext.Truncate(); |
michael@0 | 6423 | } |
michael@0 | 6424 | current = nextFrame; |
michael@0 | 6425 | offset = nextFrameOffset; |
michael@0 | 6426 | // Jumping a line is equivalent to encountering whitespace |
michael@0 | 6427 | if (wordSelectEatSpace && jumpedLine) |
michael@0 | 6428 | state.SetSawBeforeType(); |
michael@0 | 6429 | } |
michael@0 | 6430 | } |
michael@0 | 6431 | } |
michael@0 | 6432 | |
michael@0 | 6433 | // Set outputs |
michael@0 | 6434 | range = GetRangeForFrame(current); |
michael@0 | 6435 | aPos->mResultFrame = current; |
michael@0 | 6436 | aPos->mResultContent = range.content; |
michael@0 | 6437 | // Output offset is relative to content, not frame |
michael@0 | 6438 | aPos->mContentOffset = (offset < 0 ? range.end : range.start + offset) + offsetAdjustment; |
michael@0 | 6439 | break; |
michael@0 | 6440 | } |
michael@0 | 6441 | case eSelectLine : |
michael@0 | 6442 | { |
michael@0 | 6443 | nsAutoLineIterator iter; |
michael@0 | 6444 | nsIFrame *blockFrame = this; |
michael@0 | 6445 | |
michael@0 | 6446 | while (NS_FAILED(result)){ |
michael@0 | 6447 | int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame); |
michael@0 | 6448 | if (thisLine < 0) |
michael@0 | 6449 | return NS_ERROR_FAILURE; |
michael@0 | 6450 | iter = blockFrame->GetLineIterator(); |
michael@0 | 6451 | NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?"); |
michael@0 | 6452 | result = NS_OK; |
michael@0 | 6453 | |
michael@0 | 6454 | int edgeCase = 0;//no edge case. this should look at thisLine |
michael@0 | 6455 | |
michael@0 | 6456 | bool doneLooping = false;//tells us when no more block frames hit. |
michael@0 | 6457 | //this part will find a frame or a block frame. if it's a block frame |
michael@0 | 6458 | //it will "drill down" to find a viable frame or it will return an error. |
michael@0 | 6459 | nsIFrame *lastFrame = this; |
michael@0 | 6460 | do { |
michael@0 | 6461 | result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(), |
michael@0 | 6462 | aPos, |
michael@0 | 6463 | blockFrame, |
michael@0 | 6464 | thisLine, |
michael@0 | 6465 | edgeCase //start from thisLine |
michael@0 | 6466 | ); |
michael@0 | 6467 | if (NS_SUCCEEDED(result) && (!aPos->mResultFrame || aPos->mResultFrame == lastFrame))//we came back to same spot! keep going |
michael@0 | 6468 | { |
michael@0 | 6469 | aPos->mResultFrame = nullptr; |
michael@0 | 6470 | if (aPos->mDirection == eDirPrevious) |
michael@0 | 6471 | thisLine--; |
michael@0 | 6472 | else |
michael@0 | 6473 | thisLine++; |
michael@0 | 6474 | } |
michael@0 | 6475 | else //if failure or success with different frame. |
michael@0 | 6476 | doneLooping = true; //do not continue with while loop |
michael@0 | 6477 | |
michael@0 | 6478 | lastFrame = aPos->mResultFrame; //set last frame |
michael@0 | 6479 | |
michael@0 | 6480 | if (NS_SUCCEEDED(result) && aPos->mResultFrame |
michael@0 | 6481 | && blockFrame != aPos->mResultFrame)// make sure block element is not the same as the one we had before |
michael@0 | 6482 | { |
michael@0 | 6483 | /* SPECIAL CHECK FOR TABLE NAVIGATION |
michael@0 | 6484 | tables need to navigate also and the frame that supports it is nsTableRowGroupFrame which is INSIDE |
michael@0 | 6485 | nsTableOuterFrame. if we have stumbled onto an nsTableOuter we need to drill into nsTableRowGroup |
michael@0 | 6486 | if we hit a header or footer that's ok just go into them, |
michael@0 | 6487 | */ |
michael@0 | 6488 | bool searchTableBool = false; |
michael@0 | 6489 | if (aPos->mResultFrame->GetType() == nsGkAtoms::tableOuterFrame || |
michael@0 | 6490 | aPos->mResultFrame->GetType() == nsGkAtoms::tableCellFrame) |
michael@0 | 6491 | { |
michael@0 | 6492 | nsIFrame *frame = aPos->mResultFrame->GetFirstPrincipalChild(); |
michael@0 | 6493 | //got the table frame now |
michael@0 | 6494 | while(frame) //ok time to drill down to find iterator |
michael@0 | 6495 | { |
michael@0 | 6496 | iter = frame->GetLineIterator(); |
michael@0 | 6497 | if (iter) |
michael@0 | 6498 | { |
michael@0 | 6499 | aPos->mResultFrame = frame; |
michael@0 | 6500 | searchTableBool = true; |
michael@0 | 6501 | result = NS_OK; |
michael@0 | 6502 | break; //while(frame) |
michael@0 | 6503 | } |
michael@0 | 6504 | result = NS_ERROR_FAILURE; |
michael@0 | 6505 | frame = frame->GetFirstPrincipalChild(); |
michael@0 | 6506 | } |
michael@0 | 6507 | } |
michael@0 | 6508 | |
michael@0 | 6509 | if (!searchTableBool) { |
michael@0 | 6510 | iter = aPos->mResultFrame->GetLineIterator(); |
michael@0 | 6511 | result = iter ? NS_OK : NS_ERROR_FAILURE; |
michael@0 | 6512 | } |
michael@0 | 6513 | if (NS_SUCCEEDED(result) && iter)//we've struck another block element! |
michael@0 | 6514 | { |
michael@0 | 6515 | doneLooping = false; |
michael@0 | 6516 | if (aPos->mDirection == eDirPrevious) |
michael@0 | 6517 | edgeCase = 1;//far edge, search from end backwards |
michael@0 | 6518 | else |
michael@0 | 6519 | edgeCase = -1;//near edge search from beginning onwards |
michael@0 | 6520 | thisLine=0;//this line means nothing now. |
michael@0 | 6521 | //everything else means something so keep looking "inside" the block |
michael@0 | 6522 | blockFrame = aPos->mResultFrame; |
michael@0 | 6523 | |
michael@0 | 6524 | } |
michael@0 | 6525 | else |
michael@0 | 6526 | { |
michael@0 | 6527 | result = NS_OK;//THIS is to mean that everything is ok to the containing while loop |
michael@0 | 6528 | break; |
michael@0 | 6529 | } |
michael@0 | 6530 | } |
michael@0 | 6531 | } while (!doneLooping); |
michael@0 | 6532 | } |
michael@0 | 6533 | return result; |
michael@0 | 6534 | } |
michael@0 | 6535 | |
michael@0 | 6536 | case eSelectParagraph: |
michael@0 | 6537 | return PeekOffsetParagraph(aPos); |
michael@0 | 6538 | |
michael@0 | 6539 | case eSelectBeginLine: |
michael@0 | 6540 | case eSelectEndLine: |
michael@0 | 6541 | { |
michael@0 | 6542 | // Adjusted so that the caret can't get confused when content changes |
michael@0 | 6543 | nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this); |
michael@0 | 6544 | int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame); |
michael@0 | 6545 | if (thisLine < 0) |
michael@0 | 6546 | return NS_ERROR_FAILURE; |
michael@0 | 6547 | nsAutoLineIterator it = blockFrame->GetLineIterator(); |
michael@0 | 6548 | NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?"); |
michael@0 | 6549 | |
michael@0 | 6550 | int32_t lineFrameCount; |
michael@0 | 6551 | nsIFrame *firstFrame; |
michael@0 | 6552 | nsRect usedRect; |
michael@0 | 6553 | uint32_t lineFlags; |
michael@0 | 6554 | nsIFrame* baseFrame = nullptr; |
michael@0 | 6555 | bool endOfLine = (eSelectEndLine == aPos->mAmount); |
michael@0 | 6556 | |
michael@0 | 6557 | if (aPos->mVisual && PresContext()->BidiEnabled()) { |
michael@0 | 6558 | bool lineIsRTL = it->GetDirection(); |
michael@0 | 6559 | bool isReordered; |
michael@0 | 6560 | nsIFrame *lastFrame; |
michael@0 | 6561 | result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); |
michael@0 | 6562 | baseFrame = endOfLine ? lastFrame : firstFrame; |
michael@0 | 6563 | if (baseFrame) { |
michael@0 | 6564 | nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame); |
michael@0 | 6565 | // If the direction of the frame on the edge is opposite to that of the line, |
michael@0 | 6566 | // we'll need to drill down to its opposite end, so reverse endOfLine. |
michael@0 | 6567 | if ((embeddingLevel & 1) == !lineIsRTL) |
michael@0 | 6568 | endOfLine = !endOfLine; |
michael@0 | 6569 | } |
michael@0 | 6570 | } else { |
michael@0 | 6571 | it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags); |
michael@0 | 6572 | |
michael@0 | 6573 | nsIFrame* frame = firstFrame; |
michael@0 | 6574 | for (int32_t count = lineFrameCount; count; |
michael@0 | 6575 | --count, frame = frame->GetNextSibling()) { |
michael@0 | 6576 | if (!frame->IsGeneratedContentFrame()) { |
michael@0 | 6577 | baseFrame = frame; |
michael@0 | 6578 | if (!endOfLine) |
michael@0 | 6579 | break; |
michael@0 | 6580 | } |
michael@0 | 6581 | } |
michael@0 | 6582 | } |
michael@0 | 6583 | if (!baseFrame) |
michael@0 | 6584 | return NS_ERROR_FAILURE; |
michael@0 | 6585 | FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame, |
michael@0 | 6586 | endOfLine, 0); |
michael@0 | 6587 | FrameContentRange range = GetRangeForFrame(targetFrame.frame); |
michael@0 | 6588 | aPos->mResultContent = range.content; |
michael@0 | 6589 | aPos->mContentOffset = endOfLine ? range.end : range.start; |
michael@0 | 6590 | if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) { |
michael@0 | 6591 | // Do not position the caret after the terminating newline if we're |
michael@0 | 6592 | // trying to move to the end of line (see bug 596506) |
michael@0 | 6593 | --aPos->mContentOffset; |
michael@0 | 6594 | } |
michael@0 | 6595 | aPos->mResultFrame = targetFrame.frame; |
michael@0 | 6596 | aPos->mAttachForward = (aPos->mContentOffset == range.start); |
michael@0 | 6597 | if (!range.content) |
michael@0 | 6598 | return NS_ERROR_FAILURE; |
michael@0 | 6599 | return NS_OK; |
michael@0 | 6600 | } |
michael@0 | 6601 | |
michael@0 | 6602 | default: |
michael@0 | 6603 | { |
michael@0 | 6604 | NS_ASSERTION(false, "Invalid amount"); |
michael@0 | 6605 | return NS_ERROR_FAILURE; |
michael@0 | 6606 | } |
michael@0 | 6607 | } |
michael@0 | 6608 | return NS_OK; |
michael@0 | 6609 | } |
michael@0 | 6610 | |
michael@0 | 6611 | nsIFrame::FrameSearchResult |
michael@0 | 6612 | nsFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) |
michael@0 | 6613 | { |
michael@0 | 6614 | NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
michael@0 | 6615 | // Sure, we can stop right here. |
michael@0 | 6616 | return FOUND; |
michael@0 | 6617 | } |
michael@0 | 6618 | |
michael@0 | 6619 | nsIFrame::FrameSearchResult |
michael@0 | 6620 | nsFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, |
michael@0 | 6621 | bool aRespectClusters) |
michael@0 | 6622 | { |
michael@0 | 6623 | NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
michael@0 | 6624 | int32_t startOffset = *aOffset; |
michael@0 | 6625 | // A negative offset means "end of frame", which in our case means offset 1. |
michael@0 | 6626 | if (startOffset < 0) |
michael@0 | 6627 | startOffset = 1; |
michael@0 | 6628 | if (aForward == (startOffset == 0)) { |
michael@0 | 6629 | // We're before the frame and moving forward, or after it and moving backwards: |
michael@0 | 6630 | // skip to the other side and we're done. |
michael@0 | 6631 | *aOffset = 1 - startOffset; |
michael@0 | 6632 | return FOUND; |
michael@0 | 6633 | } |
michael@0 | 6634 | return CONTINUE; |
michael@0 | 6635 | } |
michael@0 | 6636 | |
michael@0 | 6637 | nsIFrame::FrameSearchResult |
michael@0 | 6638 | nsFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, |
michael@0 | 6639 | int32_t* aOffset, PeekWordState* aState) |
michael@0 | 6640 | { |
michael@0 | 6641 | NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
michael@0 | 6642 | int32_t startOffset = *aOffset; |
michael@0 | 6643 | // This isn't text, so truncate the context |
michael@0 | 6644 | aState->mContext.Truncate(); |
michael@0 | 6645 | if (startOffset < 0) |
michael@0 | 6646 | startOffset = 1; |
michael@0 | 6647 | if (aForward == (startOffset == 0)) { |
michael@0 | 6648 | // We're before the frame and moving forward, or after it and moving backwards. |
michael@0 | 6649 | // If we're looking for non-whitespace, we found it (without skipping this frame). |
michael@0 | 6650 | if (!aState->mAtStart) { |
michael@0 | 6651 | if (aState->mLastCharWasPunctuation) { |
michael@0 | 6652 | // We're not punctuation, so this is a punctuation boundary. |
michael@0 | 6653 | if (BreakWordBetweenPunctuation(aState, aForward, false, false, aIsKeyboardSelect)) |
michael@0 | 6654 | return FOUND; |
michael@0 | 6655 | } else { |
michael@0 | 6656 | // This is not a punctuation boundary. |
michael@0 | 6657 | if (aWordSelectEatSpace && aState->mSawBeforeType) |
michael@0 | 6658 | return FOUND; |
michael@0 | 6659 | } |
michael@0 | 6660 | } |
michael@0 | 6661 | // Otherwise skip to the other side and note that we encountered non-whitespace. |
michael@0 | 6662 | *aOffset = 1 - startOffset; |
michael@0 | 6663 | aState->Update(false, // not punctuation |
michael@0 | 6664 | false // not whitespace |
michael@0 | 6665 | ); |
michael@0 | 6666 | if (!aWordSelectEatSpace) |
michael@0 | 6667 | aState->SetSawBeforeType(); |
michael@0 | 6668 | } |
michael@0 | 6669 | return CONTINUE; |
michael@0 | 6670 | } |
michael@0 | 6671 | |
michael@0 | 6672 | bool |
michael@0 | 6673 | nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState, |
michael@0 | 6674 | bool aForward, |
michael@0 | 6675 | bool aPunctAfter, bool aWhitespaceAfter, |
michael@0 | 6676 | bool aIsKeyboardSelect) |
michael@0 | 6677 | { |
michael@0 | 6678 | NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation, |
michael@0 | 6679 | "Call this only at punctuation boundaries"); |
michael@0 | 6680 | if (aState->mLastCharWasWhitespace) { |
michael@0 | 6681 | // We always stop between whitespace and punctuation |
michael@0 | 6682 | return true; |
michael@0 | 6683 | } |
michael@0 | 6684 | if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) { |
michael@0 | 6685 | // When this pref is false, we never stop at a punctuation boundary unless |
michael@0 | 6686 | // it's followed by whitespace (in the relevant direction). |
michael@0 | 6687 | return aWhitespaceAfter; |
michael@0 | 6688 | } |
michael@0 | 6689 | if (!aIsKeyboardSelect) { |
michael@0 | 6690 | // mouse caret movement (e.g. word selection) always stops at every punctuation boundary |
michael@0 | 6691 | return true; |
michael@0 | 6692 | } |
michael@0 | 6693 | bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter; |
michael@0 | 6694 | if (!afterPunct) { |
michael@0 | 6695 | // keyboard caret movement only stops after punctuation (in content order) |
michael@0 | 6696 | return false; |
michael@0 | 6697 | } |
michael@0 | 6698 | // Stop only if we've seen some non-punctuation since the last whitespace; |
michael@0 | 6699 | // don't stop after punctuation that follows whitespace. |
michael@0 | 6700 | return aState->mSeenNonPunctuationSinceWhitespace; |
michael@0 | 6701 | } |
michael@0 | 6702 | |
michael@0 | 6703 | nsresult |
michael@0 | 6704 | nsFrame::CheckVisibility(nsPresContext* , int32_t , int32_t , bool , bool *, bool *) |
michael@0 | 6705 | { |
michael@0 | 6706 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 6707 | } |
michael@0 | 6708 | |
michael@0 | 6709 | |
michael@0 | 6710 | int32_t |
michael@0 | 6711 | nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainingBlock) |
michael@0 | 6712 | { |
michael@0 | 6713 | NS_ASSERTION(aFrame, "null aFrame"); |
michael@0 | 6714 | nsFrameManager* frameManager = aFrame->PresContext()->FrameManager(); |
michael@0 | 6715 | nsIFrame *blockFrame = aFrame; |
michael@0 | 6716 | nsIFrame *thisBlock; |
michael@0 | 6717 | nsAutoLineIterator it; |
michael@0 | 6718 | nsresult result = NS_ERROR_FAILURE; |
michael@0 | 6719 | while (NS_FAILED(result) && blockFrame) |
michael@0 | 6720 | { |
michael@0 | 6721 | thisBlock = blockFrame; |
michael@0 | 6722 | if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
michael@0 | 6723 | //if we are searching for a frame that is not in flow we will not find it. |
michael@0 | 6724 | //we must instead look for its placeholder |
michael@0 | 6725 | if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { |
michael@0 | 6726 | // abspos continuations don't have placeholders, get the fif |
michael@0 | 6727 | thisBlock = thisBlock->FirstInFlow(); |
michael@0 | 6728 | } |
michael@0 | 6729 | thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock); |
michael@0 | 6730 | if (!thisBlock) |
michael@0 | 6731 | return -1; |
michael@0 | 6732 | } |
michael@0 | 6733 | blockFrame = thisBlock->GetParent(); |
michael@0 | 6734 | result = NS_OK; |
michael@0 | 6735 | if (blockFrame) { |
michael@0 | 6736 | if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame) |
michael@0 | 6737 | return -1; |
michael@0 | 6738 | it = blockFrame->GetLineIterator(); |
michael@0 | 6739 | if (!it) |
michael@0 | 6740 | result = NS_ERROR_FAILURE; |
michael@0 | 6741 | } |
michael@0 | 6742 | } |
michael@0 | 6743 | if (!blockFrame || !it) |
michael@0 | 6744 | return -1; |
michael@0 | 6745 | |
michael@0 | 6746 | if (aContainingBlock) |
michael@0 | 6747 | *aContainingBlock = blockFrame; |
michael@0 | 6748 | return it->FindLineContaining(thisBlock); |
michael@0 | 6749 | } |
michael@0 | 6750 | |
michael@0 | 6751 | nsresult |
michael@0 | 6752 | nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual, |
michael@0 | 6753 | bool aJumpLines, bool aScrollViewStop, |
michael@0 | 6754 | nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine) |
michael@0 | 6755 | { |
michael@0 | 6756 | nsresult result; |
michael@0 | 6757 | |
michael@0 | 6758 | if (!aOutFrame || !aOutOffset || !aOutJumpedLine) |
michael@0 | 6759 | return NS_ERROR_NULL_POINTER; |
michael@0 | 6760 | |
michael@0 | 6761 | nsPresContext* presContext = PresContext(); |
michael@0 | 6762 | *aOutFrame = nullptr; |
michael@0 | 6763 | *aOutOffset = 0; |
michael@0 | 6764 | *aOutJumpedLine = false; |
michael@0 | 6765 | |
michael@0 | 6766 | // Find the prev/next selectable frame |
michael@0 | 6767 | bool selectable = false; |
michael@0 | 6768 | nsIFrame *traversedFrame = this; |
michael@0 | 6769 | while (!selectable) { |
michael@0 | 6770 | nsIFrame *blockFrame; |
michael@0 | 6771 | |
michael@0 | 6772 | int32_t thisLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame); |
michael@0 | 6773 | if (thisLine < 0) |
michael@0 | 6774 | return NS_ERROR_FAILURE; |
michael@0 | 6775 | |
michael@0 | 6776 | nsAutoLineIterator it = blockFrame->GetLineIterator(); |
michael@0 | 6777 | NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?"); |
michael@0 | 6778 | |
michael@0 | 6779 | bool atLineEdge; |
michael@0 | 6780 | nsIFrame *firstFrame; |
michael@0 | 6781 | nsIFrame *lastFrame; |
michael@0 | 6782 | if (aVisual && presContext->BidiEnabled()) { |
michael@0 | 6783 | bool lineIsRTL = it->GetDirection(); |
michael@0 | 6784 | bool isReordered; |
michael@0 | 6785 | result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); |
michael@0 | 6786 | nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame; |
michael@0 | 6787 | if (*framePtr) { |
michael@0 | 6788 | nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr); |
michael@0 | 6789 | if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) == |
michael@0 | 6790 | (aDirection == eDirPrevious)) { |
michael@0 | 6791 | nsFrame::GetFirstLeaf(presContext, framePtr); |
michael@0 | 6792 | } else { |
michael@0 | 6793 | nsFrame::GetLastLeaf(presContext, framePtr); |
michael@0 | 6794 | } |
michael@0 | 6795 | atLineEdge = *framePtr == traversedFrame; |
michael@0 | 6796 | } else { |
michael@0 | 6797 | atLineEdge = true; |
michael@0 | 6798 | } |
michael@0 | 6799 | } else { |
michael@0 | 6800 | nsRect nonUsedRect; |
michael@0 | 6801 | int32_t lineFrameCount; |
michael@0 | 6802 | uint32_t lineFlags; |
michael@0 | 6803 | result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect, |
michael@0 | 6804 | &lineFlags); |
michael@0 | 6805 | if (NS_FAILED(result)) |
michael@0 | 6806 | return result; |
michael@0 | 6807 | |
michael@0 | 6808 | if (aDirection == eDirPrevious) { |
michael@0 | 6809 | nsFrame::GetFirstLeaf(presContext, &firstFrame); |
michael@0 | 6810 | atLineEdge = firstFrame == traversedFrame; |
michael@0 | 6811 | } else { // eDirNext |
michael@0 | 6812 | lastFrame = firstFrame; |
michael@0 | 6813 | for (;lineFrameCount > 1;lineFrameCount --){ |
michael@0 | 6814 | result = it->GetNextSiblingOnLine(lastFrame, thisLine); |
michael@0 | 6815 | if (NS_FAILED(result) || !lastFrame){ |
michael@0 | 6816 | NS_ERROR("should not be reached nsFrame"); |
michael@0 | 6817 | return NS_ERROR_FAILURE; |
michael@0 | 6818 | } |
michael@0 | 6819 | } |
michael@0 | 6820 | nsFrame::GetLastLeaf(presContext, &lastFrame); |
michael@0 | 6821 | atLineEdge = lastFrame == traversedFrame; |
michael@0 | 6822 | } |
michael@0 | 6823 | } |
michael@0 | 6824 | |
michael@0 | 6825 | if (atLineEdge) { |
michael@0 | 6826 | *aOutJumpedLine = true; |
michael@0 | 6827 | if (!aJumpLines) |
michael@0 | 6828 | return NS_ERROR_FAILURE; //we are done. cannot jump lines |
michael@0 | 6829 | } |
michael@0 | 6830 | |
michael@0 | 6831 | nsCOMPtr<nsIFrameEnumerator> frameTraversal; |
michael@0 | 6832 | result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), |
michael@0 | 6833 | presContext, traversedFrame, |
michael@0 | 6834 | eLeaf, |
michael@0 | 6835 | aVisual && presContext->BidiEnabled(), |
michael@0 | 6836 | aScrollViewStop, |
michael@0 | 6837 | true // aFollowOOFs |
michael@0 | 6838 | ); |
michael@0 | 6839 | if (NS_FAILED(result)) |
michael@0 | 6840 | return result; |
michael@0 | 6841 | |
michael@0 | 6842 | if (aDirection == eDirNext) |
michael@0 | 6843 | frameTraversal->Next(); |
michael@0 | 6844 | else |
michael@0 | 6845 | frameTraversal->Prev(); |
michael@0 | 6846 | |
michael@0 | 6847 | traversedFrame = frameTraversal->CurrentItem(); |
michael@0 | 6848 | if (!traversedFrame) |
michael@0 | 6849 | return NS_ERROR_FAILURE; |
michael@0 | 6850 | traversedFrame->IsSelectable(&selectable, nullptr); |
michael@0 | 6851 | } // while (!selectable) |
michael@0 | 6852 | |
michael@0 | 6853 | *aOutOffset = (aDirection == eDirNext) ? 0 : -1; |
michael@0 | 6854 | |
michael@0 | 6855 | if (aVisual) { |
michael@0 | 6856 | uint8_t newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame); |
michael@0 | 6857 | uint8_t newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame); |
michael@0 | 6858 | if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end |
michael@0 | 6859 | *aOutOffset = -1 - *aOutOffset; |
michael@0 | 6860 | } |
michael@0 | 6861 | *aOutFrame = traversedFrame; |
michael@0 | 6862 | return NS_OK; |
michael@0 | 6863 | } |
michael@0 | 6864 | |
michael@0 | 6865 | nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const |
michael@0 | 6866 | { |
michael@0 | 6867 | nsPoint offset(0,0); |
michael@0 | 6868 | for (const nsIFrame *f = this; f; f = f->GetParent()) { |
michael@0 | 6869 | if (f->HasView()) { |
michael@0 | 6870 | if (aOffset) |
michael@0 | 6871 | *aOffset = offset; |
michael@0 | 6872 | return f->GetView(); |
michael@0 | 6873 | } |
michael@0 | 6874 | offset += f->GetPosition(); |
michael@0 | 6875 | } |
michael@0 | 6876 | |
michael@0 | 6877 | NS_NOTREACHED("No view on any parent? How did that happen?"); |
michael@0 | 6878 | return nullptr; |
michael@0 | 6879 | } |
michael@0 | 6880 | |
michael@0 | 6881 | |
michael@0 | 6882 | /* virtual */ void |
michael@0 | 6883 | nsFrame::ChildIsDirty(nsIFrame* aChild) |
michael@0 | 6884 | { |
michael@0 | 6885 | NS_NOTREACHED("should never be called on a frame that doesn't inherit from " |
michael@0 | 6886 | "nsContainerFrame"); |
michael@0 | 6887 | } |
michael@0 | 6888 | |
michael@0 | 6889 | |
michael@0 | 6890 | #ifdef ACCESSIBILITY |
michael@0 | 6891 | a11y::AccType |
michael@0 | 6892 | nsFrame::AccessibleType() |
michael@0 | 6893 | { |
michael@0 | 6894 | return a11y::eNoType; |
michael@0 | 6895 | } |
michael@0 | 6896 | #endif |
michael@0 | 6897 | |
michael@0 | 6898 | NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty, |
michael@0 | 6899 | nsIFrame::DestroyOverflowAreas) |
michael@0 | 6900 | |
michael@0 | 6901 | bool |
michael@0 | 6902 | nsIFrame::ClearOverflowRects() |
michael@0 | 6903 | { |
michael@0 | 6904 | if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) { |
michael@0 | 6905 | return false; |
michael@0 | 6906 | } |
michael@0 | 6907 | if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
michael@0 | 6908 | Properties().Delete(OverflowAreasProperty()); |
michael@0 | 6909 | } |
michael@0 | 6910 | mOverflow.mType = NS_FRAME_OVERFLOW_NONE; |
michael@0 | 6911 | return true; |
michael@0 | 6912 | } |
michael@0 | 6913 | |
michael@0 | 6914 | /** Create or retrieve the previously stored overflow area, if the frame does |
michael@0 | 6915 | * not overflow and no creation is required return nullptr. |
michael@0 | 6916 | * @return pointer to the overflow area rectangle |
michael@0 | 6917 | */ |
michael@0 | 6918 | nsOverflowAreas* |
michael@0 | 6919 | nsIFrame::GetOverflowAreasProperty() |
michael@0 | 6920 | { |
michael@0 | 6921 | FrameProperties props = Properties(); |
michael@0 | 6922 | nsOverflowAreas *overflow = |
michael@0 | 6923 | static_cast<nsOverflowAreas*>(props.Get(OverflowAreasProperty())); |
michael@0 | 6924 | |
michael@0 | 6925 | if (overflow) { |
michael@0 | 6926 | return overflow; // the property already exists |
michael@0 | 6927 | } |
michael@0 | 6928 | |
michael@0 | 6929 | // The property isn't set yet, so allocate a new rect, set the property, |
michael@0 | 6930 | // and return the newly allocated rect |
michael@0 | 6931 | overflow = new nsOverflowAreas; |
michael@0 | 6932 | props.Set(OverflowAreasProperty(), overflow); |
michael@0 | 6933 | return overflow; |
michael@0 | 6934 | } |
michael@0 | 6935 | |
michael@0 | 6936 | /** Set the overflowArea rect, storing it as deltas or a separate rect |
michael@0 | 6937 | * depending on its size in relation to the primary frame rect. |
michael@0 | 6938 | */ |
michael@0 | 6939 | bool |
michael@0 | 6940 | nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas) |
michael@0 | 6941 | { |
michael@0 | 6942 | if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { |
michael@0 | 6943 | nsOverflowAreas *overflow = |
michael@0 | 6944 | static_cast<nsOverflowAreas*>(Properties().Get(OverflowAreasProperty())); |
michael@0 | 6945 | bool changed = *overflow != aOverflowAreas; |
michael@0 | 6946 | *overflow = aOverflowAreas; |
michael@0 | 6947 | |
michael@0 | 6948 | // Don't bother with converting to the deltas form if we already |
michael@0 | 6949 | // have a property. |
michael@0 | 6950 | return changed; |
michael@0 | 6951 | } |
michael@0 | 6952 | |
michael@0 | 6953 | const nsRect& vis = aOverflowAreas.VisualOverflow(); |
michael@0 | 6954 | uint32_t l = -vis.x, // left edge: positive delta is leftwards |
michael@0 | 6955 | t = -vis.y, // top: positive is upwards |
michael@0 | 6956 | r = vis.XMost() - mRect.width, // right: positive is rightwards |
michael@0 | 6957 | b = vis.YMost() - mRect.height; // bottom: positive is downwards |
michael@0 | 6958 | if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) && |
michael@0 | 6959 | l <= NS_FRAME_OVERFLOW_DELTA_MAX && |
michael@0 | 6960 | t <= NS_FRAME_OVERFLOW_DELTA_MAX && |
michael@0 | 6961 | r <= NS_FRAME_OVERFLOW_DELTA_MAX && |
michael@0 | 6962 | b <= NS_FRAME_OVERFLOW_DELTA_MAX && |
michael@0 | 6963 | // we have to check these against zero because we *never* want to |
michael@0 | 6964 | // set a frame as having no overflow in this function. This is |
michael@0 | 6965 | // because FinishAndStoreOverflow calls this function prior to |
michael@0 | 6966 | // SetRect based on whether the overflow areas match aNewSize. |
michael@0 | 6967 | // In the case where the overflow areas exactly match mRect but |
michael@0 | 6968 | // do not match aNewSize, we need to store overflow in a property |
michael@0 | 6969 | // so that our eventual SetRect/SetSize will know that it has to |
michael@0 | 6970 | // reset our overflow areas. |
michael@0 | 6971 | (l | t | r | b) != 0) { |
michael@0 | 6972 | VisualDeltas oldDeltas = mOverflow.mVisualDeltas; |
michael@0 | 6973 | // It's a "small" overflow area so we store the deltas for each edge |
michael@0 | 6974 | // directly in the frame, rather than allocating a separate rect. |
michael@0 | 6975 | // If they're all zero, that's fine; we're setting things to |
michael@0 | 6976 | // no-overflow. |
michael@0 | 6977 | mOverflow.mVisualDeltas.mLeft = l; |
michael@0 | 6978 | mOverflow.mVisualDeltas.mTop = t; |
michael@0 | 6979 | mOverflow.mVisualDeltas.mRight = r; |
michael@0 | 6980 | mOverflow.mVisualDeltas.mBottom = b; |
michael@0 | 6981 | // There was no scrollable overflow before, and there isn't now. |
michael@0 | 6982 | return oldDeltas != mOverflow.mVisualDeltas; |
michael@0 | 6983 | } else { |
michael@0 | 6984 | bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) || |
michael@0 | 6985 | !aOverflowAreas.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas()); |
michael@0 | 6986 | |
michael@0 | 6987 | // it's a large overflow area that we need to store as a property |
michael@0 | 6988 | mOverflow.mType = NS_FRAME_OVERFLOW_LARGE; |
michael@0 | 6989 | nsOverflowAreas* overflow = GetOverflowAreasProperty(); |
michael@0 | 6990 | NS_ASSERTION(overflow, "should have created areas"); |
michael@0 | 6991 | *overflow = aOverflowAreas; |
michael@0 | 6992 | return changed; |
michael@0 | 6993 | } |
michael@0 | 6994 | } |
michael@0 | 6995 | |
michael@0 | 6996 | inline bool |
michael@0 | 6997 | IsInlineFrame(nsIFrame *aFrame) |
michael@0 | 6998 | { |
michael@0 | 6999 | nsIAtom *type = aFrame->GetType(); |
michael@0 | 7000 | return type == nsGkAtoms::inlineFrame; |
michael@0 | 7001 | } |
michael@0 | 7002 | |
michael@0 | 7003 | /** |
michael@0 | 7004 | * Compute the union of the border boxes of aFrame and its descendants, |
michael@0 | 7005 | * in aFrame's coordinate space (if aApplyTransform is false) or its |
michael@0 | 7006 | * post-transform coordinate space (if aApplyTransform is true). |
michael@0 | 7007 | */ |
michael@0 | 7008 | static nsRect |
michael@0 | 7009 | UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform, |
michael@0 | 7010 | const nsSize* aSizeOverride = nullptr, |
michael@0 | 7011 | const nsOverflowAreas* aOverflowOverride = nullptr) |
michael@0 | 7012 | { |
michael@0 | 7013 | const nsRect bounds(nsPoint(0, 0), |
michael@0 | 7014 | aSizeOverride ? *aSizeOverride : aFrame->GetSize()); |
michael@0 | 7015 | |
michael@0 | 7016 | // Start from our border-box, transformed. See comment below about |
michael@0 | 7017 | // transform of children. |
michael@0 | 7018 | nsRect u; |
michael@0 | 7019 | bool doTransform = aApplyTransform && aFrame->IsTransformed(); |
michael@0 | 7020 | if (doTransform) { |
michael@0 | 7021 | u = nsDisplayTransform::TransformRect(bounds, aFrame, |
michael@0 | 7022 | nsPoint(0, 0), &bounds); |
michael@0 | 7023 | } else { |
michael@0 | 7024 | u = bounds; |
michael@0 | 7025 | } |
michael@0 | 7026 | |
michael@0 | 7027 | // Only iterate through the children if the overflow areas suggest |
michael@0 | 7028 | // that we might need to, and if the frame doesn't clip its overflow |
michael@0 | 7029 | // anyway. |
michael@0 | 7030 | if (aOverflowOverride) { |
michael@0 | 7031 | if (!doTransform && |
michael@0 | 7032 | bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) && |
michael@0 | 7033 | bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) { |
michael@0 | 7034 | return u; |
michael@0 | 7035 | } |
michael@0 | 7036 | } else { |
michael@0 | 7037 | if (!doTransform && |
michael@0 | 7038 | bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) && |
michael@0 | 7039 | bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) { |
michael@0 | 7040 | return u; |
michael@0 | 7041 | } |
michael@0 | 7042 | } |
michael@0 | 7043 | const nsStyleDisplay* disp = aFrame->StyleDisplay(); |
michael@0 | 7044 | nsIAtom* fType = aFrame->GetType(); |
michael@0 | 7045 | if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) || |
michael@0 | 7046 | fType == nsGkAtoms::scrollFrame || |
michael@0 | 7047 | fType == nsGkAtoms::svgOuterSVGFrame) { |
michael@0 | 7048 | return u; |
michael@0 | 7049 | } |
michael@0 | 7050 | |
michael@0 | 7051 | nsRect clipPropClipRect; |
michael@0 | 7052 | bool hasClipPropClip = |
michael@0 | 7053 | aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size()); |
michael@0 | 7054 | |
michael@0 | 7055 | // Iterate over all children except pop-ups. |
michael@0 | 7056 | const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList | |
michael@0 | 7057 | nsIFrame::kSelectPopupList); |
michael@0 | 7058 | for (nsIFrame::ChildListIterator childLists(aFrame); |
michael@0 | 7059 | !childLists.IsDone(); childLists.Next()) { |
michael@0 | 7060 | if (skip.Contains(childLists.CurrentID())) { |
michael@0 | 7061 | continue; |
michael@0 | 7062 | } |
michael@0 | 7063 | |
michael@0 | 7064 | nsFrameList children = childLists.CurrentList(); |
michael@0 | 7065 | for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { |
michael@0 | 7066 | nsIFrame* child = e.get(); |
michael@0 | 7067 | // Note that passing |true| for aApplyTransform when |
michael@0 | 7068 | // child->Preserves3D() is incorrect if our aApplyTransform is |
michael@0 | 7069 | // false... but the opposite would be as well. This is because |
michael@0 | 7070 | // elements within a preserve-3d scene are always transformed up |
michael@0 | 7071 | // to the top of the scene. This means we don't have a |
michael@0 | 7072 | // mechanism for getting a transform up to an intermediate point |
michael@0 | 7073 | // within the scene. We choose to over-transform rather than |
michael@0 | 7074 | // under-transform because this is consistent with other |
michael@0 | 7075 | // overflow areas. |
michael@0 | 7076 | nsRect childRect = UnionBorderBoxes(child, true) + |
michael@0 | 7077 | child->GetPosition(); |
michael@0 | 7078 | |
michael@0 | 7079 | if (hasClipPropClip) { |
michael@0 | 7080 | // Intersect with the clip before transforming. |
michael@0 | 7081 | childRect.IntersectRect(childRect, clipPropClipRect); |
michael@0 | 7082 | } |
michael@0 | 7083 | |
michael@0 | 7084 | // Note that we transform each child separately according to |
michael@0 | 7085 | // aFrame's transform, and then union, which gives a different |
michael@0 | 7086 | // (smaller) result from unioning and then transforming the |
michael@0 | 7087 | // union. This doesn't match the way we handle overflow areas |
michael@0 | 7088 | // with 2-D transforms, though it does match the way we handle |
michael@0 | 7089 | // overflow areas in preserve-3d 3-D scenes. |
michael@0 | 7090 | if (doTransform && !child->Preserves3D()) { |
michael@0 | 7091 | childRect = nsDisplayTransform::TransformRect(childRect, aFrame, |
michael@0 | 7092 | nsPoint(0, 0), &bounds); |
michael@0 | 7093 | } |
michael@0 | 7094 | u.UnionRectEdges(u, childRect); |
michael@0 | 7095 | } |
michael@0 | 7096 | } |
michael@0 | 7097 | |
michael@0 | 7098 | return u; |
michael@0 | 7099 | } |
michael@0 | 7100 | |
michael@0 | 7101 | static void |
michael@0 | 7102 | ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas, |
michael@0 | 7103 | const nsSize& aNewSize) |
michael@0 | 7104 | { |
michael@0 | 7105 | const nsStyleOutline* outline = aFrame->StyleOutline(); |
michael@0 | 7106 | if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) { |
michael@0 | 7107 | return; |
michael@0 | 7108 | } |
michael@0 | 7109 | |
michael@0 | 7110 | nscoord width; |
michael@0 | 7111 | DebugOnly<bool> result = outline->GetOutlineWidth(width); |
michael@0 | 7112 | NS_ASSERTION(result, "GetOutlineWidth had no cached outline width"); |
michael@0 | 7113 | if (width <= 0) { |
michael@0 | 7114 | return; |
michael@0 | 7115 | } |
michael@0 | 7116 | |
michael@0 | 7117 | // When the outline property is set on :-moz-anonymous-block or |
michael@0 | 7118 | // :-moz-anonymous-positioned-block pseudo-elements, it inherited |
michael@0 | 7119 | // that outline from the inline that was broken because it |
michael@0 | 7120 | // contained a block. In that case, we don't want a really wide |
michael@0 | 7121 | // outline if the block inside the inline is narrow, so union the |
michael@0 | 7122 | // actual contents of the anonymous blocks. |
michael@0 | 7123 | nsIFrame *frameForArea = aFrame; |
michael@0 | 7124 | do { |
michael@0 | 7125 | nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo(); |
michael@0 | 7126 | if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock && |
michael@0 | 7127 | pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock) |
michael@0 | 7128 | break; |
michael@0 | 7129 | // If we're done, we really want it and all its later siblings. |
michael@0 | 7130 | frameForArea = frameForArea->GetFirstPrincipalChild(); |
michael@0 | 7131 | NS_ASSERTION(frameForArea, "anonymous block with no children?"); |
michael@0 | 7132 | } while (frameForArea); |
michael@0 | 7133 | |
michael@0 | 7134 | // Find the union of the border boxes of all descendants, or in |
michael@0 | 7135 | // the block-in-inline case, all descendants we care about. |
michael@0 | 7136 | // |
michael@0 | 7137 | // Note that the interesting perspective-related cases are taken |
michael@0 | 7138 | // care of by the code that handles those issues for overflow |
michael@0 | 7139 | // calling FinishAndStoreOverflow again, which in turn calls this |
michael@0 | 7140 | // function again. We still need to deal with preserve-3d a bit. |
michael@0 | 7141 | nsRect innerRect; |
michael@0 | 7142 | if (frameForArea == aFrame) { |
michael@0 | 7143 | innerRect = UnionBorderBoxes(aFrame, false, &aNewSize, &aOverflowAreas); |
michael@0 | 7144 | } else { |
michael@0 | 7145 | for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) { |
michael@0 | 7146 | nsRect r(UnionBorderBoxes(frameForArea, true)); |
michael@0 | 7147 | |
michael@0 | 7148 | // Adjust for offsets transforms up to aFrame's pre-transform |
michael@0 | 7149 | // (i.e., normal) coordinate space; see comments in |
michael@0 | 7150 | // UnionBorderBoxes for some of the subtlety here. |
michael@0 | 7151 | for (nsIFrame *f = frameForArea, *parent = f->GetParent(); |
michael@0 | 7152 | /* see middle of loop */; |
michael@0 | 7153 | f = parent, parent = f->GetParent()) { |
michael@0 | 7154 | r += f->GetPosition(); |
michael@0 | 7155 | if (parent == aFrame) { |
michael@0 | 7156 | break; |
michael@0 | 7157 | } |
michael@0 | 7158 | if (parent->IsTransformed() && !f->Preserves3D()) { |
michael@0 | 7159 | r = nsDisplayTransform::TransformRect(r, parent, nsPoint(0, 0)); |
michael@0 | 7160 | } |
michael@0 | 7161 | } |
michael@0 | 7162 | |
michael@0 | 7163 | innerRect.UnionRect(innerRect, r); |
michael@0 | 7164 | } |
michael@0 | 7165 | } |
michael@0 | 7166 | |
michael@0 | 7167 | aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(), |
michael@0 | 7168 | new nsRect(innerRect)); |
michael@0 | 7169 | |
michael@0 | 7170 | nscoord offset = outline->mOutlineOffset; |
michael@0 | 7171 | nscoord inflateBy = std::max(width + offset, 0); |
michael@0 | 7172 | |
michael@0 | 7173 | // Keep this code (and the storing of properties just above) in |
michael@0 | 7174 | // sync with GetOutlineInnerRect in nsCSSRendering.cpp. |
michael@0 | 7175 | nsRect outerRect(innerRect); |
michael@0 | 7176 | outerRect.Inflate(inflateBy, inflateBy); |
michael@0 | 7177 | |
michael@0 | 7178 | nsRect& vo = aOverflowAreas.VisualOverflow(); |
michael@0 | 7179 | vo.UnionRectEdges(vo, outerRect); |
michael@0 | 7180 | } |
michael@0 | 7181 | |
michael@0 | 7182 | bool |
michael@0 | 7183 | nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, |
michael@0 | 7184 | nsSize aNewSize, nsSize* aOldSize) |
michael@0 | 7185 | { |
michael@0 | 7186 | NS_ASSERTION(FrameMaintainsOverflow(this), |
michael@0 | 7187 | "Don't call - overflow rects not maintained on these SVG frames"); |
michael@0 | 7188 | |
michael@0 | 7189 | nsRect bounds(nsPoint(0, 0), aNewSize); |
michael@0 | 7190 | // Store the passed in overflow area if we are a preserve-3d frame or we have |
michael@0 | 7191 | // a transform, and it's not just the frame bounds. |
michael@0 | 7192 | if (Preserves3D() || HasPerspective() || IsTransformed()) { |
michael@0 | 7193 | if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || |
michael@0 | 7194 | !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) { |
michael@0 | 7195 | |
michael@0 | 7196 | nsOverflowAreas* initial = |
michael@0 | 7197 | static_cast<nsOverflowAreas*>(Properties().Get(nsIFrame::InitialOverflowProperty())); |
michael@0 | 7198 | if (!initial) { |
michael@0 | 7199 | Properties().Set(nsIFrame::InitialOverflowProperty(), |
michael@0 | 7200 | new nsOverflowAreas(aOverflowAreas)); |
michael@0 | 7201 | } else if (initial != &aOverflowAreas) { |
michael@0 | 7202 | *initial = aOverflowAreas; |
michael@0 | 7203 | } |
michael@0 | 7204 | } |
michael@0 | 7205 | #ifdef DEBUG |
michael@0 | 7206 | Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), nullptr); |
michael@0 | 7207 | #endif |
michael@0 | 7208 | } else { |
michael@0 | 7209 | #ifdef DEBUG |
michael@0 | 7210 | Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied()); |
michael@0 | 7211 | #endif |
michael@0 | 7212 | } |
michael@0 | 7213 | |
michael@0 | 7214 | // This is now called FinishAndStoreOverflow() instead of |
michael@0 | 7215 | // StoreOverflow() because frame-generic ways of adding overflow |
michael@0 | 7216 | // can happen here, e.g. CSS2 outline and native theme. |
michael@0 | 7217 | // If the overflow area width or height is nscoord_MAX, then a |
michael@0 | 7218 | // saturating union may have encounted an overflow, so the overflow may not |
michael@0 | 7219 | // contain the frame border-box. Don't warn in that case. |
michael@0 | 7220 | // Don't warn for SVG either, since SVG doesn't need the overflow area |
michael@0 | 7221 | // to contain the frame bounds. |
michael@0 | 7222 | NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
michael@0 | 7223 | DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype); |
michael@0 | 7224 | NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 || |
michael@0 | 7225 | r->width == nscoord_MAX || r->height == nscoord_MAX || |
michael@0 | 7226 | (mState & NS_FRAME_SVG_LAYOUT) || |
michael@0 | 7227 | r->Contains(nsRect(nsPoint(0,0), aNewSize)), |
michael@0 | 7228 | "Computed overflow area must contain frame bounds"); |
michael@0 | 7229 | } |
michael@0 | 7230 | |
michael@0 | 7231 | // If we clip our children, clear accumulated overflow area. The |
michael@0 | 7232 | // children are actually clipped to the padding-box, but since the |
michael@0 | 7233 | // overflow area should include the entire border-box, just set it to |
michael@0 | 7234 | // the border-box here. |
michael@0 | 7235 | const nsStyleDisplay* disp = StyleDisplay(); |
michael@0 | 7236 | NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) == |
michael@0 | 7237 | (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP), |
michael@0 | 7238 | "If one overflow is clip, the other should be too"); |
michael@0 | 7239 | if (nsFrame::ShouldApplyOverflowClipping(this, disp)) { |
michael@0 | 7240 | // The contents are actually clipped to the padding area |
michael@0 | 7241 | aOverflowAreas.SetAllTo(bounds); |
michael@0 | 7242 | } |
michael@0 | 7243 | |
michael@0 | 7244 | // Overflow area must always include the frame's top-left and bottom-right, |
michael@0 | 7245 | // even if the frame rect is empty (so we can scroll to those positions). |
michael@0 | 7246 | // Pending a real fix for bug 426879, don't do this for inline frames |
michael@0 | 7247 | // with zero width. |
michael@0 | 7248 | // Do not do this for SVG either, since it will usually massively increase |
michael@0 | 7249 | // the area unnecessarily. |
michael@0 | 7250 | if ((aNewSize.width != 0 || !IsInlineFrame(this)) && |
michael@0 | 7251 | !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) { |
michael@0 | 7252 | NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
michael@0 | 7253 | nsRect& o = aOverflowAreas.Overflow(otype); |
michael@0 | 7254 | o.UnionRectEdges(o, bounds); |
michael@0 | 7255 | } |
michael@0 | 7256 | } |
michael@0 | 7257 | |
michael@0 | 7258 | // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background, |
michael@0 | 7259 | // so we add theme background overflow here so it's not clipped. |
michael@0 | 7260 | if (!IsBoxWrapped() && IsThemed(disp)) { |
michael@0 | 7261 | nsRect r(bounds); |
michael@0 | 7262 | nsPresContext *presContext = PresContext(); |
michael@0 | 7263 | if (presContext->GetTheme()-> |
michael@0 | 7264 | GetWidgetOverflow(presContext->DeviceContext(), this, |
michael@0 | 7265 | disp->mAppearance, &r)) { |
michael@0 | 7266 | nsRect& vo = aOverflowAreas.VisualOverflow(); |
michael@0 | 7267 | vo.UnionRectEdges(vo, r); |
michael@0 | 7268 | } |
michael@0 | 7269 | } |
michael@0 | 7270 | |
michael@0 | 7271 | ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize); |
michael@0 | 7272 | |
michael@0 | 7273 | // Nothing in here should affect scrollable overflow. |
michael@0 | 7274 | aOverflowAreas.VisualOverflow() = |
michael@0 | 7275 | ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize); |
michael@0 | 7276 | |
michael@0 | 7277 | // Absolute position clipping |
michael@0 | 7278 | nsRect clipPropClipRect; |
michael@0 | 7279 | bool hasClipPropClip = GetClipPropClipRect(disp, &clipPropClipRect, aNewSize); |
michael@0 | 7280 | if (hasClipPropClip) { |
michael@0 | 7281 | NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
michael@0 | 7282 | nsRect& o = aOverflowAreas.Overflow(otype); |
michael@0 | 7283 | o.IntersectRect(o, clipPropClipRect); |
michael@0 | 7284 | } |
michael@0 | 7285 | } |
michael@0 | 7286 | |
michael@0 | 7287 | /* If we're transformed, transform the overflow rect by the current transformation. */ |
michael@0 | 7288 | bool hasTransform = IsTransformed(); |
michael@0 | 7289 | nsSize oldSize = aOldSize ? *aOldSize : mRect.Size(); |
michael@0 | 7290 | bool sizeChanged = (oldSize != aNewSize); |
michael@0 | 7291 | if (hasTransform) { |
michael@0 | 7292 | Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(), |
michael@0 | 7293 | new nsOverflowAreas(aOverflowAreas)); |
michael@0 | 7294 | /* Since our size might not actually have been computed yet, we need to make sure that we use the |
michael@0 | 7295 | * correct dimensions by overriding the stored bounding rectangle with the value the caller has |
michael@0 | 7296 | * ensured us we'll use. |
michael@0 | 7297 | */ |
michael@0 | 7298 | nsRect newBounds(nsPoint(0, 0), aNewSize); |
michael@0 | 7299 | // Transform affects both overflow areas. |
michael@0 | 7300 | NS_FOR_FRAME_OVERFLOW_TYPES(otype) { |
michael@0 | 7301 | nsRect& o = aOverflowAreas.Overflow(otype); |
michael@0 | 7302 | o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds); |
michael@0 | 7303 | } |
michael@0 | 7304 | if (Preserves3DChildren()) { |
michael@0 | 7305 | ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds); |
michael@0 | 7306 | } else if (sizeChanged && ChildrenHavePerspective()) { |
michael@0 | 7307 | RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds); |
michael@0 | 7308 | } |
michael@0 | 7309 | } else { |
michael@0 | 7310 | Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty()); |
michael@0 | 7311 | if (ChildrenHavePerspective() && sizeChanged) { |
michael@0 | 7312 | nsRect newBounds(nsPoint(0, 0), aNewSize); |
michael@0 | 7313 | RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds); |
michael@0 | 7314 | } |
michael@0 | 7315 | } |
michael@0 | 7316 | |
michael@0 | 7317 | bool anyOverflowChanged; |
michael@0 | 7318 | if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) { |
michael@0 | 7319 | anyOverflowChanged = SetOverflowAreas(aOverflowAreas); |
michael@0 | 7320 | } else { |
michael@0 | 7321 | anyOverflowChanged = ClearOverflowRects(); |
michael@0 | 7322 | } |
michael@0 | 7323 | |
michael@0 | 7324 | if (anyOverflowChanged) { |
michael@0 | 7325 | nsSVGEffects::InvalidateDirectRenderingObservers(this); |
michael@0 | 7326 | } |
michael@0 | 7327 | return anyOverflowChanged; |
michael@0 | 7328 | } |
michael@0 | 7329 | |
michael@0 | 7330 | void |
michael@0 | 7331 | nsIFrame::RecomputePerspectiveChildrenOverflow(const nsStyleContext* aStartStyle, const nsRect* aBounds) |
michael@0 | 7332 | { |
michael@0 | 7333 | // Children may check our size when getting our transform, make sure it's valid. |
michael@0 | 7334 | nsSize oldSize = GetSize(); |
michael@0 | 7335 | if (aBounds) { |
michael@0 | 7336 | SetSize(aBounds->Size()); |
michael@0 | 7337 | } |
michael@0 | 7338 | nsIFrame::ChildListIterator lists(this); |
michael@0 | 7339 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 7340 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 7341 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 7342 | nsIFrame* child = childFrames.get(); |
michael@0 | 7343 | if (!FrameMaintainsOverflow(child)) { |
michael@0 | 7344 | continue; // frame does not maintain overflow rects |
michael@0 | 7345 | } |
michael@0 | 7346 | if (child->HasPerspective()) { |
michael@0 | 7347 | nsOverflowAreas* overflow = |
michael@0 | 7348 | static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty())); |
michael@0 | 7349 | nsRect bounds(nsPoint(0, 0), child->GetSize()); |
michael@0 | 7350 | if (overflow) { |
michael@0 | 7351 | nsOverflowAreas overflowCopy = *overflow; |
michael@0 | 7352 | child->FinishAndStoreOverflow(overflowCopy, bounds.Size()); |
michael@0 | 7353 | } else { |
michael@0 | 7354 | nsOverflowAreas boundsOverflow; |
michael@0 | 7355 | boundsOverflow.SetAllTo(bounds); |
michael@0 | 7356 | child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); |
michael@0 | 7357 | } |
michael@0 | 7358 | } else if (child->StyleContext()->GetParent() == aStartStyle || |
michael@0 | 7359 | child->StyleContext() == aStartStyle) { |
michael@0 | 7360 | // If a frame is using perspective, then the size used to compute |
michael@0 | 7361 | // perspective-origin is the size of the frame belonging to its parent |
michael@0 | 7362 | // style context. We must find any descendant frames using our size |
michael@0 | 7363 | // (by recurse into frames with the same style context, or a direct |
michael@0 | 7364 | // child style context) to update their overflow rects too. |
michael@0 | 7365 | child->RecomputePerspectiveChildrenOverflow(aStartStyle, nullptr); |
michael@0 | 7366 | } |
michael@0 | 7367 | } |
michael@0 | 7368 | } |
michael@0 | 7369 | // Restore our old size just in case something depends on this elesewhere. |
michael@0 | 7370 | SetSize(oldSize); |
michael@0 | 7371 | } |
michael@0 | 7372 | |
michael@0 | 7373 | /* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on |
michael@0 | 7374 | * the mRect value for their parents (since we use their transform, and transform |
michael@0 | 7375 | * depends on this for transform-origin etc). These weren't necessarily correct |
michael@0 | 7376 | * when we reflowed initially, so walk over all preserve-3d children and repeat the |
michael@0 | 7377 | * overflow calculation. |
michael@0 | 7378 | */ |
michael@0 | 7379 | static void |
michael@0 | 7380 | RecomputePreserve3DChildrenOverflow(nsIFrame* aFrame, const nsRect* aBounds) |
michael@0 | 7381 | { |
michael@0 | 7382 | // Children may check our size when getting our transform, make sure it's valid. |
michael@0 | 7383 | nsSize oldSize = aFrame->GetSize(); |
michael@0 | 7384 | if (aBounds) { |
michael@0 | 7385 | aFrame->SetSize(aBounds->Size()); |
michael@0 | 7386 | } |
michael@0 | 7387 | nsIFrame::ChildListIterator lists(aFrame); |
michael@0 | 7388 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 7389 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 7390 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 7391 | nsIFrame* child = childFrames.get(); |
michael@0 | 7392 | if (!FrameMaintainsOverflow(child)) { |
michael@0 | 7393 | continue; // frame does not maintain overflow rects |
michael@0 | 7394 | } |
michael@0 | 7395 | if (child->Preserves3DChildren()) { |
michael@0 | 7396 | RecomputePreserve3DChildrenOverflow(child, nullptr); |
michael@0 | 7397 | } else if (child->Preserves3D()) { |
michael@0 | 7398 | nsOverflowAreas* overflow = |
michael@0 | 7399 | static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty())); |
michael@0 | 7400 | nsRect bounds(nsPoint(0, 0), child->GetSize()); |
michael@0 | 7401 | if (overflow) { |
michael@0 | 7402 | nsOverflowAreas overflowCopy = *overflow; |
michael@0 | 7403 | child->FinishAndStoreOverflow(overflowCopy, bounds.Size()); |
michael@0 | 7404 | } else { |
michael@0 | 7405 | nsOverflowAreas boundsOverflow; |
michael@0 | 7406 | boundsOverflow.SetAllTo(bounds); |
michael@0 | 7407 | child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); |
michael@0 | 7408 | } |
michael@0 | 7409 | } |
michael@0 | 7410 | } |
michael@0 | 7411 | } |
michael@0 | 7412 | // Restore our old size just in case something depends on this elesewhere. |
michael@0 | 7413 | aFrame->SetSize(oldSize); |
michael@0 | 7414 | |
michael@0 | 7415 | // Only repeat computing our overflow in recursive calls since the initial caller is still |
michael@0 | 7416 | // in the middle of doing this and we don't want an infinite loop. |
michael@0 | 7417 | if (!aBounds) { |
michael@0 | 7418 | nsOverflowAreas* overflow = |
michael@0 | 7419 | static_cast<nsOverflowAreas*>(aFrame->Properties().Get(nsIFrame::InitialOverflowProperty())); |
michael@0 | 7420 | nsRect bounds(nsPoint(0, 0), aFrame->GetSize()); |
michael@0 | 7421 | if (overflow) { |
michael@0 | 7422 | nsOverflowAreas overflowCopy = *overflow; |
michael@0 | 7423 | overflowCopy.UnionAllWith(bounds); |
michael@0 | 7424 | aFrame->FinishAndStoreOverflow(overflowCopy, bounds.Size()); |
michael@0 | 7425 | } else { |
michael@0 | 7426 | nsOverflowAreas boundsOverflow; |
michael@0 | 7427 | boundsOverflow.SetAllTo(bounds); |
michael@0 | 7428 | aFrame->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); |
michael@0 | 7429 | } |
michael@0 | 7430 | } |
michael@0 | 7431 | } |
michael@0 | 7432 | |
michael@0 | 7433 | void |
michael@0 | 7434 | nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds) |
michael@0 | 7435 | { |
michael@0 | 7436 | // When we are preserving 3d we need to iterate over all children separately. |
michael@0 | 7437 | // If the child also preserves 3d then their overflow will already been in our |
michael@0 | 7438 | // coordinate space, otherwise we need to transform. |
michael@0 | 7439 | |
michael@0 | 7440 | // If we're the top frame in a preserve 3d chain then we need to recalculate the overflow |
michael@0 | 7441 | // areas of all our children since they will have used our size/offset which was invalid at |
michael@0 | 7442 | // the time. |
michael@0 | 7443 | if (!Preserves3D()) { |
michael@0 | 7444 | RecomputePreserve3DChildrenOverflow(this, &aBounds); |
michael@0 | 7445 | } |
michael@0 | 7446 | |
michael@0 | 7447 | nsRect childVisual; |
michael@0 | 7448 | nsRect childScrollable; |
michael@0 | 7449 | nsIFrame::ChildListIterator lists(this); |
michael@0 | 7450 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 7451 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 7452 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 7453 | nsIFrame* child = childFrames.get(); |
michael@0 | 7454 | nsPoint offset = child->GetPosition(); |
michael@0 | 7455 | nsRect visual = child->GetVisualOverflowRect(); |
michael@0 | 7456 | nsRect scrollable = child->GetScrollableOverflowRect(); |
michael@0 | 7457 | visual.MoveBy(offset); |
michael@0 | 7458 | scrollable.MoveBy(offset); |
michael@0 | 7459 | if (child->Preserves3D()) { |
michael@0 | 7460 | childVisual = childVisual.Union(visual); |
michael@0 | 7461 | childScrollable = childScrollable.Union(scrollable); |
michael@0 | 7462 | } else { |
michael@0 | 7463 | childVisual = |
michael@0 | 7464 | childVisual.Union(nsDisplayTransform::TransformRect(visual, |
michael@0 | 7465 | this, nsPoint(0,0), &aBounds)); |
michael@0 | 7466 | childScrollable = |
michael@0 | 7467 | childScrollable.Union(nsDisplayTransform::TransformRect(scrollable, |
michael@0 | 7468 | this, nsPoint(0,0), &aBounds)); |
michael@0 | 7469 | } |
michael@0 | 7470 | } |
michael@0 | 7471 | } |
michael@0 | 7472 | |
michael@0 | 7473 | aOverflowAreas.Overflow(eVisualOverflow) = aOverflowAreas.Overflow(eVisualOverflow).Union(childVisual); |
michael@0 | 7474 | aOverflowAreas.Overflow(eScrollableOverflow) = aOverflowAreas.Overflow(eScrollableOverflow).Union(childScrollable); |
michael@0 | 7475 | } |
michael@0 | 7476 | |
michael@0 | 7477 | void |
michael@0 | 7478 | nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas, |
michael@0 | 7479 | nsIFrame* aChildFrame) |
michael@0 | 7480 | { |
michael@0 | 7481 | aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() + |
michael@0 | 7482 | aChildFrame->GetPosition()); |
michael@0 | 7483 | } |
michael@0 | 7484 | |
michael@0 | 7485 | /** |
michael@0 | 7486 | * This function takes a frame that is part of a block-in-inline split, |
michael@0 | 7487 | * and _if_ that frame is an anonymous block created by an ib split it |
michael@0 | 7488 | * returns the block's preceding inline. This is needed because the |
michael@0 | 7489 | * split inline's style context is the parent of the anonymous block's |
michael@0 | 7490 | * style context. |
michael@0 | 7491 | * |
michael@0 | 7492 | * If aFrame is not an anonymous block, null is returned. |
michael@0 | 7493 | */ |
michael@0 | 7494 | static nsIFrame* |
michael@0 | 7495 | GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) |
michael@0 | 7496 | { |
michael@0 | 7497 | NS_PRECONDITION(aFrame, "Must have a non-null frame!"); |
michael@0 | 7498 | NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT, |
michael@0 | 7499 | "GetIBSplitSibling should only be called on ib-split frames"); |
michael@0 | 7500 | |
michael@0 | 7501 | nsIAtom* type = aFrame->StyleContext()->GetPseudo(); |
michael@0 | 7502 | if (type != nsCSSAnonBoxes::mozAnonymousBlock && |
michael@0 | 7503 | type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) { |
michael@0 | 7504 | // it's not an anonymous block |
michael@0 | 7505 | return nullptr; |
michael@0 | 7506 | } |
michael@0 | 7507 | |
michael@0 | 7508 | // Find the first continuation of the frame. (Ugh. This ends up |
michael@0 | 7509 | // being O(N^2) when it is called O(N) times.) |
michael@0 | 7510 | aFrame = aFrame->FirstContinuation(); |
michael@0 | 7511 | |
michael@0 | 7512 | /* |
michael@0 | 7513 | * Now look up the nsGkAtoms::IBSplitPrevSibling |
michael@0 | 7514 | * property. |
michael@0 | 7515 | */ |
michael@0 | 7516 | nsIFrame *ibSplitSibling = static_cast<nsIFrame*> |
michael@0 | 7517 | (aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())); |
michael@0 | 7518 | NS_ASSERTION(ibSplitSibling, "Broken frame tree?"); |
michael@0 | 7519 | return ibSplitSibling; |
michael@0 | 7520 | } |
michael@0 | 7521 | |
michael@0 | 7522 | /** |
michael@0 | 7523 | * Get the parent, corrected for the mangled frame tree resulting from |
michael@0 | 7524 | * having a block within an inline. The result only differs from the |
michael@0 | 7525 | * result of |GetParent| when |GetParent| returns an anonymous block |
michael@0 | 7526 | * that was created for an element that was 'display: inline' because |
michael@0 | 7527 | * that element contained a block. |
michael@0 | 7528 | * |
michael@0 | 7529 | * Also skip anonymous scrolled-content parents; inherit directly from the |
michael@0 | 7530 | * outer scroll frame. |
michael@0 | 7531 | */ |
michael@0 | 7532 | static nsIFrame* |
michael@0 | 7533 | GetCorrectedParent(const nsIFrame* aFrame) |
michael@0 | 7534 | { |
michael@0 | 7535 | nsIFrame *parent = aFrame->GetParent(); |
michael@0 | 7536 | if (!parent) { |
michael@0 | 7537 | return nullptr; |
michael@0 | 7538 | } |
michael@0 | 7539 | |
michael@0 | 7540 | // Outer tables are always anon boxes; if we're in here for an outer |
michael@0 | 7541 | // table, that actually means its the _inner_ table that wants to |
michael@0 | 7542 | // know its parent. So get the pseudo of the inner in that case. |
michael@0 | 7543 | nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo(); |
michael@0 | 7544 | if (pseudo == nsCSSAnonBoxes::tableOuter) { |
michael@0 | 7545 | pseudo = aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo(); |
michael@0 | 7546 | } |
michael@0 | 7547 | return nsFrame::CorrectStyleParentFrame(parent, pseudo); |
michael@0 | 7548 | } |
michael@0 | 7549 | |
michael@0 | 7550 | /* static */ |
michael@0 | 7551 | nsIFrame* |
michael@0 | 7552 | nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent, |
michael@0 | 7553 | nsIAtom* aChildPseudo) |
michael@0 | 7554 | { |
michael@0 | 7555 | NS_PRECONDITION(aProspectiveParent, "Must have a prospective parent"); |
michael@0 | 7556 | |
michael@0 | 7557 | // Anon boxes are parented to their actual parent already, except |
michael@0 | 7558 | // for non-elements. Those should not be treated as an anon box. |
michael@0 | 7559 | if (aChildPseudo && aChildPseudo != nsCSSAnonBoxes::mozNonElement && |
michael@0 | 7560 | nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) { |
michael@0 | 7561 | NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock && |
michael@0 | 7562 | aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock, |
michael@0 | 7563 | "Should have dealt with kids that have " |
michael@0 | 7564 | "NS_FRAME_PART_OF_IBSPLIT elsewhere"); |
michael@0 | 7565 | return aProspectiveParent; |
michael@0 | 7566 | } |
michael@0 | 7567 | |
michael@0 | 7568 | // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out |
michael@0 | 7569 | // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause |
michael@0 | 7570 | // style data to be out of sync with the frame tree. |
michael@0 | 7571 | nsIFrame* parent = aProspectiveParent; |
michael@0 | 7572 | do { |
michael@0 | 7573 | if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) { |
michael@0 | 7574 | nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent); |
michael@0 | 7575 | |
michael@0 | 7576 | if (sibling) { |
michael@0 | 7577 | // |parent| was a block in an {ib} split; use the inline as |
michael@0 | 7578 | // |the style parent. |
michael@0 | 7579 | parent = sibling; |
michael@0 | 7580 | } |
michael@0 | 7581 | } |
michael@0 | 7582 | |
michael@0 | 7583 | nsIAtom* parentPseudo = parent->StyleContext()->GetPseudo(); |
michael@0 | 7584 | if (!parentPseudo || |
michael@0 | 7585 | (!nsCSSAnonBoxes::IsAnonBox(parentPseudo) && |
michael@0 | 7586 | // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for |
michael@0 | 7587 | // aChildPseudo (even though that's not a valid pseudo-type) just to |
michael@0 | 7588 | // trigger this behavior of walking up to the nearest non-pseudo |
michael@0 | 7589 | // ancestor. |
michael@0 | 7590 | aChildPseudo != nsGkAtoms::placeholderFrame)) { |
michael@0 | 7591 | return parent; |
michael@0 | 7592 | } |
michael@0 | 7593 | |
michael@0 | 7594 | parent = parent->GetParent(); |
michael@0 | 7595 | } while (parent); |
michael@0 | 7596 | |
michael@0 | 7597 | if (aProspectiveParent->StyleContext()->GetPseudo() == |
michael@0 | 7598 | nsCSSAnonBoxes::viewportScroll) { |
michael@0 | 7599 | // aProspectiveParent is the scrollframe for a viewport |
michael@0 | 7600 | // and the kids are the anonymous scrollbars |
michael@0 | 7601 | return aProspectiveParent; |
michael@0 | 7602 | } |
michael@0 | 7603 | |
michael@0 | 7604 | // We can get here if the root element is absolutely positioned. |
michael@0 | 7605 | // We can't test for this very accurately, but it can only happen |
michael@0 | 7606 | // when the prospective parent is a canvas frame. |
michael@0 | 7607 | NS_ASSERTION(aProspectiveParent->GetType() == nsGkAtoms::canvasFrame, |
michael@0 | 7608 | "Should have found a parent before this"); |
michael@0 | 7609 | return nullptr; |
michael@0 | 7610 | } |
michael@0 | 7611 | |
michael@0 | 7612 | nsIFrame* |
michael@0 | 7613 | nsFrame::DoGetParentStyleContextFrame() const |
michael@0 | 7614 | { |
michael@0 | 7615 | if (mContent && !mContent->GetParent() && |
michael@0 | 7616 | !StyleContext()->GetPseudo()) { |
michael@0 | 7617 | // we're a frame for the root. We have no style context parent. |
michael@0 | 7618 | return nullptr; |
michael@0 | 7619 | } |
michael@0 | 7620 | |
michael@0 | 7621 | if (!(mState & NS_FRAME_OUT_OF_FLOW)) { |
michael@0 | 7622 | /* |
michael@0 | 7623 | * If this frame is an anonymous block created when an inline with a block |
michael@0 | 7624 | * inside it got split, then the parent style context is on its preceding |
michael@0 | 7625 | * inline. We can get to it using GetIBSplitSiblingForAnonymousBlock. |
michael@0 | 7626 | */ |
michael@0 | 7627 | if (mState & NS_FRAME_PART_OF_IBSPLIT) { |
michael@0 | 7628 | nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this); |
michael@0 | 7629 | if (ibSplitSibling) { |
michael@0 | 7630 | return ibSplitSibling; |
michael@0 | 7631 | } |
michael@0 | 7632 | } |
michael@0 | 7633 | |
michael@0 | 7634 | // If this frame is one of the blocks that split an inline, we must |
michael@0 | 7635 | // return the "special" inline parent, i.e., the parent that this |
michael@0 | 7636 | // frame would have if we didn't mangle the frame structure. |
michael@0 | 7637 | return GetCorrectedParent(this); |
michael@0 | 7638 | } |
michael@0 | 7639 | |
michael@0 | 7640 | // We're an out-of-flow frame. For out-of-flow frames, we must |
michael@0 | 7641 | // resolve underneath the placeholder's parent. The placeholder is |
michael@0 | 7642 | // reached from the first-in-flow. |
michael@0 | 7643 | nsIFrame* placeholder = PresContext()->FrameManager()-> |
michael@0 | 7644 | GetPlaceholderFrameFor(FirstInFlow()); |
michael@0 | 7645 | if (!placeholder) { |
michael@0 | 7646 | NS_NOTREACHED("no placeholder frame for out-of-flow frame"); |
michael@0 | 7647 | return GetCorrectedParent(this); |
michael@0 | 7648 | } |
michael@0 | 7649 | return placeholder->GetParentStyleContextFrame(); |
michael@0 | 7650 | } |
michael@0 | 7651 | |
michael@0 | 7652 | void |
michael@0 | 7653 | nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) |
michael@0 | 7654 | { |
michael@0 | 7655 | if (!aFrame || !*aFrame) |
michael@0 | 7656 | return; |
michael@0 | 7657 | nsIFrame *child = *aFrame; |
michael@0 | 7658 | //if we are a block frame then go for the last line of 'this' |
michael@0 | 7659 | while (1){ |
michael@0 | 7660 | child = child->GetFirstPrincipalChild(); |
michael@0 | 7661 | if (!child) |
michael@0 | 7662 | return;//nothing to do |
michael@0 | 7663 | nsIFrame* siblingFrame; |
michael@0 | 7664 | nsIContent* content; |
michael@0 | 7665 | //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove* |
michael@0 | 7666 | //see bug 278197 comment #12 #13 for details |
michael@0 | 7667 | while ((siblingFrame = child->GetNextSibling()) && |
michael@0 | 7668 | (content = siblingFrame->GetContent()) && |
michael@0 | 7669 | !content->IsRootOfNativeAnonymousSubtree()) |
michael@0 | 7670 | child = siblingFrame; |
michael@0 | 7671 | *aFrame = child; |
michael@0 | 7672 | } |
michael@0 | 7673 | } |
michael@0 | 7674 | |
michael@0 | 7675 | void |
michael@0 | 7676 | nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) |
michael@0 | 7677 | { |
michael@0 | 7678 | if (!aFrame || !*aFrame) |
michael@0 | 7679 | return; |
michael@0 | 7680 | nsIFrame *child = *aFrame; |
michael@0 | 7681 | while (1){ |
michael@0 | 7682 | child = child->GetFirstPrincipalChild(); |
michael@0 | 7683 | if (!child) |
michael@0 | 7684 | return;//nothing to do |
michael@0 | 7685 | *aFrame = child; |
michael@0 | 7686 | } |
michael@0 | 7687 | } |
michael@0 | 7688 | |
michael@0 | 7689 | /* virtual */ bool |
michael@0 | 7690 | nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse) |
michael@0 | 7691 | { |
michael@0 | 7692 | int32_t tabIndex = -1; |
michael@0 | 7693 | if (aTabIndex) { |
michael@0 | 7694 | *aTabIndex = -1; // Default for early return is not focusable |
michael@0 | 7695 | } |
michael@0 | 7696 | bool isFocusable = false; |
michael@0 | 7697 | |
michael@0 | 7698 | if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors()) { |
michael@0 | 7699 | const nsStyleUserInterface* ui = StyleUserInterface(); |
michael@0 | 7700 | if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE && |
michael@0 | 7701 | ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) { |
michael@0 | 7702 | // Pass in default tabindex of -1 for nonfocusable and 0 for focusable |
michael@0 | 7703 | tabIndex = 0; |
michael@0 | 7704 | } |
michael@0 | 7705 | isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse); |
michael@0 | 7706 | if (!isFocusable && !aWithMouse && |
michael@0 | 7707 | GetType() == nsGkAtoms::scrollFrame && |
michael@0 | 7708 | mContent->IsHTML() && |
michael@0 | 7709 | !mContent->IsRootOfNativeAnonymousSubtree() && |
michael@0 | 7710 | mContent->GetParent() && |
michael@0 | 7711 | !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { |
michael@0 | 7712 | // Elements with scrollable view are focusable with script & tabbable |
michael@0 | 7713 | // Otherwise you couldn't scroll them with keyboard, which is |
michael@0 | 7714 | // an accessibility issue (e.g. Section 508 rules) |
michael@0 | 7715 | // However, we don't make them to be focusable with the mouse, |
michael@0 | 7716 | // because the extra focus outlines are considered unnecessarily ugly. |
michael@0 | 7717 | // When clicked on, the selection position within the element |
michael@0 | 7718 | // will be enough to make them keyboard scrollable. |
michael@0 | 7719 | nsIScrollableFrame *scrollFrame = do_QueryFrame(this); |
michael@0 | 7720 | if (scrollFrame && |
michael@0 | 7721 | scrollFrame->GetScrollbarStyles() != ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) && |
michael@0 | 7722 | !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) { |
michael@0 | 7723 | // Scroll bars will be used for overflow |
michael@0 | 7724 | isFocusable = true; |
michael@0 | 7725 | tabIndex = 0; |
michael@0 | 7726 | } |
michael@0 | 7727 | } |
michael@0 | 7728 | } |
michael@0 | 7729 | |
michael@0 | 7730 | if (aTabIndex) { |
michael@0 | 7731 | *aTabIndex = tabIndex; |
michael@0 | 7732 | } |
michael@0 | 7733 | return isFocusable; |
michael@0 | 7734 | } |
michael@0 | 7735 | |
michael@0 | 7736 | /** |
michael@0 | 7737 | * @return true if this text frame ends with a newline character which is |
michael@0 | 7738 | * treated as preformatted. It should return false if this is not a text frame. |
michael@0 | 7739 | */ |
michael@0 | 7740 | bool |
michael@0 | 7741 | nsIFrame::HasSignificantTerminalNewline() const |
michael@0 | 7742 | { |
michael@0 | 7743 | return false; |
michael@0 | 7744 | } |
michael@0 | 7745 | |
michael@0 | 7746 | static uint8_t |
michael@0 | 7747 | ConvertSVGDominantBaselineToVerticalAlign(uint8_t aDominantBaseline) |
michael@0 | 7748 | { |
michael@0 | 7749 | // Most of these are approximate mappings. |
michael@0 | 7750 | switch (aDominantBaseline) { |
michael@0 | 7751 | case NS_STYLE_DOMINANT_BASELINE_HANGING: |
michael@0 | 7752 | case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE: |
michael@0 | 7753 | return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP; |
michael@0 | 7754 | case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE: |
michael@0 | 7755 | case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC: |
michael@0 | 7756 | return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM; |
michael@0 | 7757 | case NS_STYLE_DOMINANT_BASELINE_CENTRAL: |
michael@0 | 7758 | case NS_STYLE_DOMINANT_BASELINE_MIDDLE: |
michael@0 | 7759 | case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL: |
michael@0 | 7760 | return NS_STYLE_VERTICAL_ALIGN_MIDDLE; |
michael@0 | 7761 | case NS_STYLE_DOMINANT_BASELINE_AUTO: |
michael@0 | 7762 | case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC: |
michael@0 | 7763 | return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
michael@0 | 7764 | case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT: |
michael@0 | 7765 | case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE: |
michael@0 | 7766 | case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE: |
michael@0 | 7767 | // These three should not simply map to 'baseline', but we don't |
michael@0 | 7768 | // support the complex baseline model that SVG 1.1 has and which |
michael@0 | 7769 | // css3-linebox now defines. |
michael@0 | 7770 | return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
michael@0 | 7771 | default: |
michael@0 | 7772 | NS_NOTREACHED("unexpected aDominantBaseline value"); |
michael@0 | 7773 | return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
michael@0 | 7774 | } |
michael@0 | 7775 | } |
michael@0 | 7776 | |
michael@0 | 7777 | uint8_t |
michael@0 | 7778 | nsIFrame::VerticalAlignEnum() const |
michael@0 | 7779 | { |
michael@0 | 7780 | if (IsSVGText()) { |
michael@0 | 7781 | uint8_t dominantBaseline; |
michael@0 | 7782 | for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) { |
michael@0 | 7783 | dominantBaseline = frame->StyleSVGReset()->mDominantBaseline; |
michael@0 | 7784 | if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO || |
michael@0 | 7785 | frame->GetType() == nsGkAtoms::svgTextFrame) { |
michael@0 | 7786 | break; |
michael@0 | 7787 | } |
michael@0 | 7788 | } |
michael@0 | 7789 | return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline); |
michael@0 | 7790 | } |
michael@0 | 7791 | |
michael@0 | 7792 | const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; |
michael@0 | 7793 | if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { |
michael@0 | 7794 | return verticalAlign.GetIntValue(); |
michael@0 | 7795 | } |
michael@0 | 7796 | |
michael@0 | 7797 | return eInvalidVerticalAlign; |
michael@0 | 7798 | } |
michael@0 | 7799 | |
michael@0 | 7800 | /* static */ |
michael@0 | 7801 | void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui, |
michael@0 | 7802 | nsIFrame::Cursor& aCursor) |
michael@0 | 7803 | { |
michael@0 | 7804 | aCursor.mCursor = ui->mCursor; |
michael@0 | 7805 | aCursor.mHaveHotspot = false; |
michael@0 | 7806 | aCursor.mHotspotX = aCursor.mHotspotY = 0.0f; |
michael@0 | 7807 | |
michael@0 | 7808 | for (nsCursorImage *item = ui->mCursorArray, |
michael@0 | 7809 | *item_end = ui->mCursorArray + ui->mCursorArrayLength; |
michael@0 | 7810 | item < item_end; ++item) { |
michael@0 | 7811 | uint32_t status; |
michael@0 | 7812 | nsresult rv = item->GetImage()->GetImageStatus(&status); |
michael@0 | 7813 | if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) { |
michael@0 | 7814 | // This is the one we want |
michael@0 | 7815 | item->GetImage()->GetImage(getter_AddRefs(aCursor.mContainer)); |
michael@0 | 7816 | aCursor.mHaveHotspot = item->mHaveHotspot; |
michael@0 | 7817 | aCursor.mHotspotX = item->mHotspotX; |
michael@0 | 7818 | aCursor.mHotspotY = item->mHotspotY; |
michael@0 | 7819 | break; |
michael@0 | 7820 | } |
michael@0 | 7821 | } |
michael@0 | 7822 | } |
michael@0 | 7823 | |
michael@0 | 7824 | NS_IMETHODIMP |
michael@0 | 7825 | nsFrame::RefreshSizeCache(nsBoxLayoutState& aState) |
michael@0 | 7826 | { |
michael@0 | 7827 | // XXXbz this comment needs some rewriting to make sense in the |
michael@0 | 7828 | // post-reflow-branch world. |
michael@0 | 7829 | |
michael@0 | 7830 | // Ok we need to compute our minimum, preferred, and maximum sizes. |
michael@0 | 7831 | // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS. |
michael@0 | 7832 | // 2) Preferred size. This is a little harder. This is the size the block would be |
michael@0 | 7833 | // if it were laid out on an infinite canvas. So we can get this by reflowing |
michael@0 | 7834 | // the block with and INTRINSIC width and height. We can also do a nice optimization |
michael@0 | 7835 | // for incremental reflow. If the reflow is incremental then we can pass a flag to |
michael@0 | 7836 | // have the block compute the preferred width for us! Preferred height can just be |
michael@0 | 7837 | // the minimum height; |
michael@0 | 7838 | // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element |
michael@0 | 7839 | // size. That would give us the width. Unfortunately you can only ask for a maxElementSize |
michael@0 | 7840 | // during an incremental reflow. So on other reflows we will just have to use 0. |
michael@0 | 7841 | // The min height on the other hand is fairly easy we need to get the largest |
michael@0 | 7842 | // line height. This can be done with the line iterator. |
michael@0 | 7843 | |
michael@0 | 7844 | // if we do have a rendering context |
michael@0 | 7845 | nsresult rv = NS_OK; |
michael@0 | 7846 | nsRenderingContext* rendContext = aState.GetRenderingContext(); |
michael@0 | 7847 | if (rendContext) { |
michael@0 | 7848 | nsPresContext* presContext = aState.PresContext(); |
michael@0 | 7849 | |
michael@0 | 7850 | // If we don't have any HTML constraints and it's a resize, then nothing in the block |
michael@0 | 7851 | // could have changed, so no refresh is necessary. |
michael@0 | 7852 | nsBoxLayoutMetrics* metrics = BoxMetrics(); |
michael@0 | 7853 | if (!DoesNeedRecalc(metrics->mBlockPrefSize)) |
michael@0 | 7854 | return NS_OK; |
michael@0 | 7855 | |
michael@0 | 7856 | // the rect we plan to size to. |
michael@0 | 7857 | nsRect rect = GetRect(); |
michael@0 | 7858 | |
michael@0 | 7859 | nsMargin bp(0,0,0,0); |
michael@0 | 7860 | GetBorderAndPadding(bp); |
michael@0 | 7861 | |
michael@0 | 7862 | { |
michael@0 | 7863 | // If we're a container for font size inflation, then shrink |
michael@0 | 7864 | // wrapping inside of us should not apply font size inflation. |
michael@0 | 7865 | AutoMaybeDisableFontInflation an(this); |
michael@0 | 7866 | |
michael@0 | 7867 | metrics->mBlockPrefSize.width = |
michael@0 | 7868 | GetPrefWidth(rendContext) + bp.LeftRight(); |
michael@0 | 7869 | metrics->mBlockMinSize.width = |
michael@0 | 7870 | GetMinWidth(rendContext) + bp.LeftRight(); |
michael@0 | 7871 | } |
michael@0 | 7872 | |
michael@0 | 7873 | // do the nasty. |
michael@0 | 7874 | const WritingMode wm = aState.OuterReflowState() ? |
michael@0 | 7875 | aState.OuterReflowState()->GetWritingMode() : GetWritingMode(); |
michael@0 | 7876 | nsHTMLReflowMetrics desiredSize(wm); |
michael@0 | 7877 | rv = BoxReflow(aState, presContext, desiredSize, rendContext, |
michael@0 | 7878 | rect.x, rect.y, |
michael@0 | 7879 | metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE); |
michael@0 | 7880 | |
michael@0 | 7881 | metrics->mBlockMinSize.height = 0; |
michael@0 | 7882 | // ok we need the max ascent of the items on the line. So to do this |
michael@0 | 7883 | // ask the block for its line iterator. Get the max ascent. |
michael@0 | 7884 | nsAutoLineIterator lines = GetLineIterator(); |
michael@0 | 7885 | if (lines) |
michael@0 | 7886 | { |
michael@0 | 7887 | metrics->mBlockMinSize.height = 0; |
michael@0 | 7888 | int count = 0; |
michael@0 | 7889 | nsIFrame* firstFrame = nullptr; |
michael@0 | 7890 | int32_t framesOnLine; |
michael@0 | 7891 | nsRect lineBounds; |
michael@0 | 7892 | uint32_t lineFlags; |
michael@0 | 7893 | |
michael@0 | 7894 | do { |
michael@0 | 7895 | lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds, &lineFlags); |
michael@0 | 7896 | |
michael@0 | 7897 | if (lineBounds.height > metrics->mBlockMinSize.height) |
michael@0 | 7898 | metrics->mBlockMinSize.height = lineBounds.height; |
michael@0 | 7899 | |
michael@0 | 7900 | count++; |
michael@0 | 7901 | } while(firstFrame); |
michael@0 | 7902 | } else { |
michael@0 | 7903 | metrics->mBlockMinSize.height = desiredSize.Height(); |
michael@0 | 7904 | } |
michael@0 | 7905 | |
michael@0 | 7906 | metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height; |
michael@0 | 7907 | |
michael@0 | 7908 | if (desiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { |
michael@0 | 7909 | if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mBlockAscent)) |
michael@0 | 7910 | metrics->mBlockAscent = GetBaseline(); |
michael@0 | 7911 | } else { |
michael@0 | 7912 | metrics->mBlockAscent = desiredSize.TopAscent(); |
michael@0 | 7913 | } |
michael@0 | 7914 | |
michael@0 | 7915 | #ifdef DEBUG_adaptor |
michael@0 | 7916 | printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics->mBlockMinSize.width, |
michael@0 | 7917 | metrics->mBlockMinSize.height, |
michael@0 | 7918 | metrics->mBlockPrefSize.width, |
michael@0 | 7919 | metrics->mBlockPrefSize.height, |
michael@0 | 7920 | metrics->mBlockAscent); |
michael@0 | 7921 | #endif |
michael@0 | 7922 | } |
michael@0 | 7923 | |
michael@0 | 7924 | return rv; |
michael@0 | 7925 | } |
michael@0 | 7926 | |
michael@0 | 7927 | /* virtual */ nsILineIterator* |
michael@0 | 7928 | nsFrame::GetLineIterator() |
michael@0 | 7929 | { |
michael@0 | 7930 | return nullptr; |
michael@0 | 7931 | } |
michael@0 | 7932 | |
michael@0 | 7933 | nsSize |
michael@0 | 7934 | nsFrame::GetPrefSize(nsBoxLayoutState& aState) |
michael@0 | 7935 | { |
michael@0 | 7936 | nsSize size(0,0); |
michael@0 | 7937 | DISPLAY_PREF_SIZE(this, size); |
michael@0 | 7938 | // If the size is cached, and there are no HTML constraints that we might |
michael@0 | 7939 | // be depending on, then we just return the cached size. |
michael@0 | 7940 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 7941 | if (!DoesNeedRecalc(metrics->mPrefSize)) { |
michael@0 | 7942 | return metrics->mPrefSize; |
michael@0 | 7943 | } |
michael@0 | 7944 | |
michael@0 | 7945 | if (IsCollapsed()) |
michael@0 | 7946 | return size; |
michael@0 | 7947 | |
michael@0 | 7948 | // get our size in CSS. |
michael@0 | 7949 | bool widthSet, heightSet; |
michael@0 | 7950 | bool completelyRedefined = nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet); |
michael@0 | 7951 | |
michael@0 | 7952 | // Refresh our caches with new sizes. |
michael@0 | 7953 | if (!completelyRedefined) { |
michael@0 | 7954 | RefreshSizeCache(aState); |
michael@0 | 7955 | nsSize blockSize = metrics->mBlockPrefSize; |
michael@0 | 7956 | |
michael@0 | 7957 | // notice we don't need to add our borders or padding |
michael@0 | 7958 | // in. That's because the block did it for us. |
michael@0 | 7959 | if (!widthSet) |
michael@0 | 7960 | size.width = blockSize.width; |
michael@0 | 7961 | if (!heightSet) |
michael@0 | 7962 | size.height = blockSize.height; |
michael@0 | 7963 | } |
michael@0 | 7964 | |
michael@0 | 7965 | metrics->mPrefSize = size; |
michael@0 | 7966 | return size; |
michael@0 | 7967 | } |
michael@0 | 7968 | |
michael@0 | 7969 | nsSize |
michael@0 | 7970 | nsFrame::GetMinSize(nsBoxLayoutState& aState) |
michael@0 | 7971 | { |
michael@0 | 7972 | nsSize size(0,0); |
michael@0 | 7973 | DISPLAY_MIN_SIZE(this, size); |
michael@0 | 7974 | // Don't use the cache if we have HTMLReflowState constraints --- they might have changed |
michael@0 | 7975 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 7976 | if (!DoesNeedRecalc(metrics->mMinSize)) { |
michael@0 | 7977 | size = metrics->mMinSize; |
michael@0 | 7978 | return size; |
michael@0 | 7979 | } |
michael@0 | 7980 | |
michael@0 | 7981 | if (IsCollapsed()) |
michael@0 | 7982 | return size; |
michael@0 | 7983 | |
michael@0 | 7984 | // get our size in CSS. |
michael@0 | 7985 | bool widthSet, heightSet; |
michael@0 | 7986 | bool completelyRedefined = |
michael@0 | 7987 | nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet); |
michael@0 | 7988 | |
michael@0 | 7989 | // Refresh our caches with new sizes. |
michael@0 | 7990 | if (!completelyRedefined) { |
michael@0 | 7991 | RefreshSizeCache(aState); |
michael@0 | 7992 | nsSize blockSize = metrics->mBlockMinSize; |
michael@0 | 7993 | |
michael@0 | 7994 | if (!widthSet) |
michael@0 | 7995 | size.width = blockSize.width; |
michael@0 | 7996 | if (!heightSet) |
michael@0 | 7997 | size.height = blockSize.height; |
michael@0 | 7998 | } |
michael@0 | 7999 | |
michael@0 | 8000 | metrics->mMinSize = size; |
michael@0 | 8001 | return size; |
michael@0 | 8002 | } |
michael@0 | 8003 | |
michael@0 | 8004 | nsSize |
michael@0 | 8005 | nsFrame::GetMaxSize(nsBoxLayoutState& aState) |
michael@0 | 8006 | { |
michael@0 | 8007 | nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
michael@0 | 8008 | DISPLAY_MAX_SIZE(this, size); |
michael@0 | 8009 | // Don't use the cache if we have HTMLReflowState constraints --- they might have changed |
michael@0 | 8010 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 8011 | if (!DoesNeedRecalc(metrics->mMaxSize)) { |
michael@0 | 8012 | size = metrics->mMaxSize; |
michael@0 | 8013 | return size; |
michael@0 | 8014 | } |
michael@0 | 8015 | |
michael@0 | 8016 | if (IsCollapsed()) |
michael@0 | 8017 | return size; |
michael@0 | 8018 | |
michael@0 | 8019 | size = nsBox::GetMaxSize(aState); |
michael@0 | 8020 | metrics->mMaxSize = size; |
michael@0 | 8021 | |
michael@0 | 8022 | return size; |
michael@0 | 8023 | } |
michael@0 | 8024 | |
michael@0 | 8025 | nscoord |
michael@0 | 8026 | nsFrame::GetFlex(nsBoxLayoutState& aState) |
michael@0 | 8027 | { |
michael@0 | 8028 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 8029 | if (!DoesNeedRecalc(metrics->mFlex)) |
michael@0 | 8030 | return metrics->mFlex; |
michael@0 | 8031 | |
michael@0 | 8032 | metrics->mFlex = nsBox::GetFlex(aState); |
michael@0 | 8033 | |
michael@0 | 8034 | return metrics->mFlex; |
michael@0 | 8035 | } |
michael@0 | 8036 | |
michael@0 | 8037 | nscoord |
michael@0 | 8038 | nsFrame::GetBoxAscent(nsBoxLayoutState& aState) |
michael@0 | 8039 | { |
michael@0 | 8040 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 8041 | if (!DoesNeedRecalc(metrics->mAscent)) |
michael@0 | 8042 | return metrics->mAscent; |
michael@0 | 8043 | |
michael@0 | 8044 | if (IsCollapsed()) { |
michael@0 | 8045 | metrics->mAscent = 0; |
michael@0 | 8046 | } else { |
michael@0 | 8047 | // Refresh our caches with new sizes. |
michael@0 | 8048 | RefreshSizeCache(aState); |
michael@0 | 8049 | metrics->mAscent = metrics->mBlockAscent; |
michael@0 | 8050 | } |
michael@0 | 8051 | |
michael@0 | 8052 | return metrics->mAscent; |
michael@0 | 8053 | } |
michael@0 | 8054 | |
michael@0 | 8055 | nsresult |
michael@0 | 8056 | nsFrame::DoLayout(nsBoxLayoutState& aState) |
michael@0 | 8057 | { |
michael@0 | 8058 | nsRect ourRect(mRect); |
michael@0 | 8059 | |
michael@0 | 8060 | nsRenderingContext* rendContext = aState.GetRenderingContext(); |
michael@0 | 8061 | nsPresContext* presContext = aState.PresContext(); |
michael@0 | 8062 | const WritingMode wm = aState.OuterReflowState() ? |
michael@0 | 8063 | aState.OuterReflowState()->GetWritingMode() : GetWritingMode(); |
michael@0 | 8064 | nsHTMLReflowMetrics desiredSize(wm); |
michael@0 | 8065 | nsresult rv = NS_OK; |
michael@0 | 8066 | |
michael@0 | 8067 | if (rendContext) { |
michael@0 | 8068 | |
michael@0 | 8069 | rv = BoxReflow(aState, presContext, desiredSize, rendContext, |
michael@0 | 8070 | ourRect.x, ourRect.y, ourRect.width, ourRect.height); |
michael@0 | 8071 | |
michael@0 | 8072 | if (IsCollapsed()) { |
michael@0 | 8073 | SetSize(nsSize(0, 0)); |
michael@0 | 8074 | } else { |
michael@0 | 8075 | |
michael@0 | 8076 | // if our child needs to be bigger. This might happend with |
michael@0 | 8077 | // wrapping text. There is no way to predict its height until we |
michael@0 | 8078 | // reflow it. Now that we know the height reshuffle upward. |
michael@0 | 8079 | if (desiredSize.Width() > ourRect.width || |
michael@0 | 8080 | desiredSize.Height() > ourRect.height) { |
michael@0 | 8081 | |
michael@0 | 8082 | #ifdef DEBUG_GROW |
michael@0 | 8083 | DumpBox(stdout); |
michael@0 | 8084 | printf(" GREW from (%d,%d) -> (%d,%d)\n", |
michael@0 | 8085 | ourRect.width, ourRect.height, |
michael@0 | 8086 | desiredSize.Width(), desiredSize.Height()); |
michael@0 | 8087 | #endif |
michael@0 | 8088 | |
michael@0 | 8089 | if (desiredSize.Width() > ourRect.width) |
michael@0 | 8090 | ourRect.width = desiredSize.Width(); |
michael@0 | 8091 | |
michael@0 | 8092 | if (desiredSize.Height() > ourRect.height) |
michael@0 | 8093 | ourRect.height = desiredSize.Height(); |
michael@0 | 8094 | } |
michael@0 | 8095 | |
michael@0 | 8096 | // ensure our size is what we think is should be. Someone could have |
michael@0 | 8097 | // reset the frame to be smaller or something dumb like that. |
michael@0 | 8098 | SetSize(ourRect.Size()); |
michael@0 | 8099 | } |
michael@0 | 8100 | } |
michael@0 | 8101 | |
michael@0 | 8102 | // Should we do this if IsCollapsed() is true? |
michael@0 | 8103 | nsSize size(GetSize()); |
michael@0 | 8104 | desiredSize.Width() = size.width; |
michael@0 | 8105 | desiredSize.Height() = size.height; |
michael@0 | 8106 | desiredSize.UnionOverflowAreasWithDesiredBounds(); |
michael@0 | 8107 | |
michael@0 | 8108 | if (HasAbsolutelyPositionedChildren()) { |
michael@0 | 8109 | // Set up a |reflowState| to pass into ReflowAbsoluteFrames |
michael@0 | 8110 | nsHTMLReflowState reflowState(aState.PresContext(), this, |
michael@0 | 8111 | aState.GetRenderingContext(), |
michael@0 | 8112 | nsSize(size.width, NS_UNCONSTRAINEDSIZE), |
michael@0 | 8113 | nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
michael@0 | 8114 | |
michael@0 | 8115 | AddStateBits(NS_FRAME_IN_REFLOW); |
michael@0 | 8116 | // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames |
michael@0 | 8117 | // (just a dummy value; hopefully that's OK) |
michael@0 | 8118 | nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; |
michael@0 | 8119 | ReflowAbsoluteFrames(aState.PresContext(), desiredSize, |
michael@0 | 8120 | reflowState, reflowStatus); |
michael@0 | 8121 | RemoveStateBits(NS_FRAME_IN_REFLOW); |
michael@0 | 8122 | } |
michael@0 | 8123 | |
michael@0 | 8124 | nsSize oldSize(ourRect.Size()); |
michael@0 | 8125 | FinishAndStoreOverflow(desiredSize.mOverflowAreas, size, &oldSize); |
michael@0 | 8126 | |
michael@0 | 8127 | SyncLayout(aState); |
michael@0 | 8128 | |
michael@0 | 8129 | return rv; |
michael@0 | 8130 | } |
michael@0 | 8131 | |
michael@0 | 8132 | nsresult |
michael@0 | 8133 | nsFrame::BoxReflow(nsBoxLayoutState& aState, |
michael@0 | 8134 | nsPresContext* aPresContext, |
michael@0 | 8135 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 8136 | nsRenderingContext* aRenderingContext, |
michael@0 | 8137 | nscoord aX, |
michael@0 | 8138 | nscoord aY, |
michael@0 | 8139 | nscoord aWidth, |
michael@0 | 8140 | nscoord aHeight, |
michael@0 | 8141 | bool aMoveFrame) |
michael@0 | 8142 | { |
michael@0 | 8143 | DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor"); |
michael@0 | 8144 | |
michael@0 | 8145 | #ifdef DEBUG_REFLOW |
michael@0 | 8146 | nsAdaptorAddIndents(); |
michael@0 | 8147 | printf("Reflowing: "); |
michael@0 | 8148 | nsFrame::ListTag(stdout, mFrame); |
michael@0 | 8149 | printf("\n"); |
michael@0 | 8150 | gIndent2++; |
michael@0 | 8151 | #endif |
michael@0 | 8152 | |
michael@0 | 8153 | nsBoxLayoutMetrics *metrics = BoxMetrics(); |
michael@0 | 8154 | nsReflowStatus status = NS_FRAME_COMPLETE; |
michael@0 | 8155 | |
michael@0 | 8156 | bool needsReflow = NS_SUBTREE_DIRTY(this); |
michael@0 | 8157 | |
michael@0 | 8158 | // if we don't need a reflow then |
michael@0 | 8159 | // lets see if we are already that size. Yes? then don't even reflow. We are done. |
michael@0 | 8160 | if (!needsReflow) { |
michael@0 | 8161 | |
michael@0 | 8162 | if (aWidth != NS_INTRINSICSIZE && aHeight != NS_INTRINSICSIZE) { |
michael@0 | 8163 | |
michael@0 | 8164 | // if the new calculated size has a 0 width or a 0 height |
michael@0 | 8165 | if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) && (aWidth == 0 || aHeight == 0)) { |
michael@0 | 8166 | needsReflow = false; |
michael@0 | 8167 | aDesiredSize.Width() = aWidth; |
michael@0 | 8168 | aDesiredSize.Height() = aHeight; |
michael@0 | 8169 | SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); |
michael@0 | 8170 | } else { |
michael@0 | 8171 | aDesiredSize.Width() = metrics->mLastSize.width; |
michael@0 | 8172 | aDesiredSize.Height() = metrics->mLastSize.height; |
michael@0 | 8173 | |
michael@0 | 8174 | // remove the margin. The rect of our child does not include it but our calculated size does. |
michael@0 | 8175 | // don't reflow if we are already the right size |
michael@0 | 8176 | if (metrics->mLastSize.width == aWidth && metrics->mLastSize.height == aHeight) |
michael@0 | 8177 | needsReflow = false; |
michael@0 | 8178 | else |
michael@0 | 8179 | needsReflow = true; |
michael@0 | 8180 | |
michael@0 | 8181 | } |
michael@0 | 8182 | } else { |
michael@0 | 8183 | // if the width or height are intrinsic alway reflow because |
michael@0 | 8184 | // we don't know what it should be. |
michael@0 | 8185 | needsReflow = true; |
michael@0 | 8186 | } |
michael@0 | 8187 | } |
michael@0 | 8188 | |
michael@0 | 8189 | // ok now reflow the child into the spacers calculated space |
michael@0 | 8190 | if (needsReflow) { |
michael@0 | 8191 | |
michael@0 | 8192 | aDesiredSize.Width() = 0; |
michael@0 | 8193 | aDesiredSize.Height() = 0; |
michael@0 | 8194 | |
michael@0 | 8195 | // create a reflow state to tell our child to flow at the given size. |
michael@0 | 8196 | |
michael@0 | 8197 | // Construct a bogus parent reflow state so that there's a usable |
michael@0 | 8198 | // containing block reflow state. |
michael@0 | 8199 | nsMargin margin(0,0,0,0); |
michael@0 | 8200 | GetMargin(margin); |
michael@0 | 8201 | |
michael@0 | 8202 | nsSize parentSize(aWidth, aHeight); |
michael@0 | 8203 | if (parentSize.height != NS_INTRINSICSIZE) |
michael@0 | 8204 | parentSize.height += margin.TopBottom(); |
michael@0 | 8205 | if (parentSize.width != NS_INTRINSICSIZE) |
michael@0 | 8206 | parentSize.width += margin.LeftRight(); |
michael@0 | 8207 | |
michael@0 | 8208 | nsIFrame *parentFrame = GetParent(); |
michael@0 | 8209 | nsFrameState savedState = parentFrame->GetStateBits(); |
michael@0 | 8210 | nsHTMLReflowState parentReflowState(aPresContext, parentFrame, |
michael@0 | 8211 | aRenderingContext, |
michael@0 | 8212 | parentSize, |
michael@0 | 8213 | nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
michael@0 | 8214 | parentFrame->RemoveStateBits(~nsFrameState(0)); |
michael@0 | 8215 | parentFrame->AddStateBits(savedState); |
michael@0 | 8216 | |
michael@0 | 8217 | // This may not do very much useful, but it's probably worth trying. |
michael@0 | 8218 | if (parentSize.width != NS_INTRINSICSIZE) |
michael@0 | 8219 | parentReflowState.SetComputedWidth(std::max(parentSize.width, 0)); |
michael@0 | 8220 | if (parentSize.height != NS_INTRINSICSIZE) |
michael@0 | 8221 | parentReflowState.SetComputedHeight(std::max(parentSize.height, 0)); |
michael@0 | 8222 | parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); |
michael@0 | 8223 | // XXX use box methods |
michael@0 | 8224 | parentFrame->GetPadding(parentReflowState.ComputedPhysicalPadding()); |
michael@0 | 8225 | parentFrame->GetBorder(parentReflowState.ComputedPhysicalBorderPadding()); |
michael@0 | 8226 | parentReflowState.ComputedPhysicalBorderPadding() += |
michael@0 | 8227 | parentReflowState.ComputedPhysicalPadding(); |
michael@0 | 8228 | |
michael@0 | 8229 | // Construct the parent chain manually since constructing it normally |
michael@0 | 8230 | // messes up dimensions. |
michael@0 | 8231 | const nsHTMLReflowState *outerReflowState = aState.OuterReflowState(); |
michael@0 | 8232 | NS_ASSERTION(!outerReflowState || outerReflowState->frame != this, |
michael@0 | 8233 | "in and out of XUL on a single frame?"); |
michael@0 | 8234 | const nsHTMLReflowState* parentRS; |
michael@0 | 8235 | if (outerReflowState && outerReflowState->frame == parentFrame) { |
michael@0 | 8236 | // We're a frame (such as a text control frame) that jumps into |
michael@0 | 8237 | // box reflow and then straight out of it on the child frame. |
michael@0 | 8238 | // This means we actually have a real parent reflow state. |
michael@0 | 8239 | // nsLayoutUtils::InflationMinFontSizeFor used to need this to be |
michael@0 | 8240 | // linked up correctly for text control frames, so do so here). |
michael@0 | 8241 | parentRS = outerReflowState; |
michael@0 | 8242 | } else { |
michael@0 | 8243 | parentRS = &parentReflowState; |
michael@0 | 8244 | } |
michael@0 | 8245 | |
michael@0 | 8246 | // XXX Is it OK that this reflow state has only one ancestor? |
michael@0 | 8247 | // (It used to have a bogus parent, skipping all the boxes). |
michael@0 | 8248 | nsSize availSize(aWidth, NS_INTRINSICSIZE); |
michael@0 | 8249 | nsHTMLReflowState reflowState(aPresContext, *parentRS, this, |
michael@0 | 8250 | availSize, -1, -1, |
michael@0 | 8251 | nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
michael@0 | 8252 | |
michael@0 | 8253 | // XXX_jwir3: This is somewhat fishy. If this is actually changing the value |
michael@0 | 8254 | // here (which it might be), then we should make sure that it's |
michael@0 | 8255 | // correct the first time around, rather than changing it later. |
michael@0 | 8256 | reflowState.mCBReflowState = parentRS; |
michael@0 | 8257 | |
michael@0 | 8258 | reflowState.mReflowDepth = aState.GetReflowDepth(); |
michael@0 | 8259 | |
michael@0 | 8260 | // mComputedWidth and mComputedHeight are content-box, not |
michael@0 | 8261 | // border-box |
michael@0 | 8262 | if (aWidth != NS_INTRINSICSIZE) { |
michael@0 | 8263 | nscoord computedWidth = |
michael@0 | 8264 | aWidth - reflowState.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 8265 | computedWidth = std::max(computedWidth, 0); |
michael@0 | 8266 | reflowState.SetComputedWidth(computedWidth); |
michael@0 | 8267 | } |
michael@0 | 8268 | |
michael@0 | 8269 | // Most child frames of box frames (e.g. subdocument or scroll frames) |
michael@0 | 8270 | // need to be constrained to the provided size and overflow as necessary. |
michael@0 | 8271 | // The one exception are block frames, because we need to know their |
michael@0 | 8272 | // natural height excluding any overflow area which may be caused by |
michael@0 | 8273 | // various CSS effects such as shadow or outline. |
michael@0 | 8274 | if (!IsFrameOfType(eBlockFrame)) { |
michael@0 | 8275 | if (aHeight != NS_INTRINSICSIZE) { |
michael@0 | 8276 | nscoord computedHeight = |
michael@0 | 8277 | aHeight - reflowState.ComputedPhysicalBorderPadding().TopBottom(); |
michael@0 | 8278 | computedHeight = std::max(computedHeight, 0); |
michael@0 | 8279 | reflowState.SetComputedHeight(computedHeight); |
michael@0 | 8280 | } else { |
michael@0 | 8281 | reflowState.SetComputedHeight( |
michael@0 | 8282 | ComputeSize(aRenderingContext, availSize, availSize.width, |
michael@0 | 8283 | nsSize(reflowState.ComputedPhysicalMargin().LeftRight(), |
michael@0 | 8284 | reflowState.ComputedPhysicalMargin().TopBottom()), |
michael@0 | 8285 | nsSize(reflowState.ComputedPhysicalBorderPadding().LeftRight() - |
michael@0 | 8286 | reflowState.ComputedPhysicalPadding().LeftRight(), |
michael@0 | 8287 | reflowState.ComputedPhysicalBorderPadding().TopBottom() - |
michael@0 | 8288 | reflowState.ComputedPhysicalPadding().TopBottom()), |
michael@0 | 8289 | nsSize(reflowState.ComputedPhysicalPadding().LeftRight(), |
michael@0 | 8290 | reflowState.ComputedPhysicalPadding().TopBottom()), |
michael@0 | 8291 | false).height |
michael@0 | 8292 | ); |
michael@0 | 8293 | } |
michael@0 | 8294 | } |
michael@0 | 8295 | |
michael@0 | 8296 | // Box layout calls SetRect before Layout, whereas non-box layout |
michael@0 | 8297 | // calls SetRect after Reflow. |
michael@0 | 8298 | // XXX Perhaps we should be doing this by twiddling the rect back to |
michael@0 | 8299 | // mLastSize before calling Reflow and then switching it back, but |
michael@0 | 8300 | // However, mLastSize can also be the size passed to BoxReflow by |
michael@0 | 8301 | // RefreshSizeCache, so that doesn't really make sense. |
michael@0 | 8302 | if (metrics->mLastSize.width != aWidth) { |
michael@0 | 8303 | reflowState.mFlags.mHResize = true; |
michael@0 | 8304 | |
michael@0 | 8305 | // When font size inflation is enabled, a horizontal resize |
michael@0 | 8306 | // requires a full reflow. See nsHTMLReflowState::InitResizeFlags |
michael@0 | 8307 | // for more details. |
michael@0 | 8308 | if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { |
michael@0 | 8309 | AddStateBits(NS_FRAME_IS_DIRTY); |
michael@0 | 8310 | } |
michael@0 | 8311 | } |
michael@0 | 8312 | if (metrics->mLastSize.height != aHeight) |
michael@0 | 8313 | reflowState.mFlags.mVResize = true; |
michael@0 | 8314 | |
michael@0 | 8315 | #ifdef DEBUG_REFLOW |
michael@0 | 8316 | nsAdaptorAddIndents(); |
michael@0 | 8317 | printf("Size=(%d,%d)\n",reflowState.ComputedWidth(), |
michael@0 | 8318 | reflowState.ComputedHeight()); |
michael@0 | 8319 | nsAdaptorAddIndents(); |
michael@0 | 8320 | nsAdaptorPrintReason(reflowState); |
michael@0 | 8321 | printf("\n"); |
michael@0 | 8322 | #endif |
michael@0 | 8323 | |
michael@0 | 8324 | // place the child and reflow |
michael@0 | 8325 | WillReflow(aPresContext); |
michael@0 | 8326 | |
michael@0 | 8327 | Reflow(aPresContext, aDesiredSize, reflowState, status); |
michael@0 | 8328 | |
michael@0 | 8329 | NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status"); |
michael@0 | 8330 | |
michael@0 | 8331 | uint32_t layoutFlags = aState.LayoutFlags(); |
michael@0 | 8332 | nsContainerFrame::FinishReflowChild(this, aPresContext, aDesiredSize, |
michael@0 | 8333 | &reflowState, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME); |
michael@0 | 8334 | |
michael@0 | 8335 | // Save the ascent. (bug 103925) |
michael@0 | 8336 | if (IsCollapsed()) { |
michael@0 | 8337 | metrics->mAscent = 0; |
michael@0 | 8338 | } else { |
michael@0 | 8339 | if (aDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { |
michael@0 | 8340 | if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mAscent)) |
michael@0 | 8341 | metrics->mAscent = GetBaseline(); |
michael@0 | 8342 | } else |
michael@0 | 8343 | metrics->mAscent = aDesiredSize.TopAscent(); |
michael@0 | 8344 | } |
michael@0 | 8345 | |
michael@0 | 8346 | } else { |
michael@0 | 8347 | aDesiredSize.SetTopAscent(metrics->mBlockAscent); |
michael@0 | 8348 | } |
michael@0 | 8349 | |
michael@0 | 8350 | #ifdef DEBUG_REFLOW |
michael@0 | 8351 | if (aHeight != NS_INTRINSICSIZE && aDesiredSize.Height() != aHeight) |
michael@0 | 8352 | { |
michael@0 | 8353 | nsAdaptorAddIndents(); |
michael@0 | 8354 | printf("*****got taller!*****\n"); |
michael@0 | 8355 | |
michael@0 | 8356 | } |
michael@0 | 8357 | if (aWidth != NS_INTRINSICSIZE && aDesiredSize.Width() != aWidth) |
michael@0 | 8358 | { |
michael@0 | 8359 | nsAdaptorAddIndents(); |
michael@0 | 8360 | printf("*****got wider!******\n"); |
michael@0 | 8361 | |
michael@0 | 8362 | } |
michael@0 | 8363 | #endif |
michael@0 | 8364 | |
michael@0 | 8365 | if (aWidth == NS_INTRINSICSIZE) |
michael@0 | 8366 | aWidth = aDesiredSize.Width(); |
michael@0 | 8367 | |
michael@0 | 8368 | if (aHeight == NS_INTRINSICSIZE) |
michael@0 | 8369 | aHeight = aDesiredSize.Height(); |
michael@0 | 8370 | |
michael@0 | 8371 | metrics->mLastSize.width = aDesiredSize.Width(); |
michael@0 | 8372 | metrics->mLastSize.height = aDesiredSize.Height(); |
michael@0 | 8373 | |
michael@0 | 8374 | #ifdef DEBUG_REFLOW |
michael@0 | 8375 | gIndent2--; |
michael@0 | 8376 | #endif |
michael@0 | 8377 | |
michael@0 | 8378 | return NS_OK; |
michael@0 | 8379 | } |
michael@0 | 8380 | |
michael@0 | 8381 | static void |
michael@0 | 8382 | DestroyBoxMetrics(void* aPropertyValue) |
michael@0 | 8383 | { |
michael@0 | 8384 | delete static_cast<nsBoxLayoutMetrics*>(aPropertyValue); |
michael@0 | 8385 | } |
michael@0 | 8386 | |
michael@0 | 8387 | NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty, DestroyBoxMetrics) |
michael@0 | 8388 | |
michael@0 | 8389 | nsBoxLayoutMetrics* |
michael@0 | 8390 | nsFrame::BoxMetrics() const |
michael@0 | 8391 | { |
michael@0 | 8392 | nsBoxLayoutMetrics* metrics = |
michael@0 | 8393 | static_cast<nsBoxLayoutMetrics*>(Properties().Get(BoxMetricsProperty())); |
michael@0 | 8394 | NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called"); |
michael@0 | 8395 | return metrics; |
michael@0 | 8396 | } |
michael@0 | 8397 | |
michael@0 | 8398 | /* static */ void |
michael@0 | 8399 | nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) |
michael@0 | 8400 | { |
michael@0 | 8401 | aFrame->AddStateBits(NS_FRAME_IN_POPUP); |
michael@0 | 8402 | |
michael@0 | 8403 | nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
michael@0 | 8404 | aFrame->GetCrossDocChildLists(&childListArray); |
michael@0 | 8405 | |
michael@0 | 8406 | nsIFrame::ChildListArrayIterator lists(childListArray); |
michael@0 | 8407 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 8408 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 8409 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 8410 | AddInPopupStateBitToDescendants(childFrames.get()); |
michael@0 | 8411 | } |
michael@0 | 8412 | } |
michael@0 | 8413 | } |
michael@0 | 8414 | |
michael@0 | 8415 | /* static */ void |
michael@0 | 8416 | nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) |
michael@0 | 8417 | { |
michael@0 | 8418 | if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) || |
michael@0 | 8419 | nsLayoutUtils::IsPopup(aFrame)) { |
michael@0 | 8420 | return; |
michael@0 | 8421 | } |
michael@0 | 8422 | |
michael@0 | 8423 | aFrame->RemoveStateBits(NS_FRAME_IN_POPUP); |
michael@0 | 8424 | |
michael@0 | 8425 | nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
michael@0 | 8426 | aFrame->GetCrossDocChildLists(&childListArray); |
michael@0 | 8427 | |
michael@0 | 8428 | nsIFrame::ChildListArrayIterator lists(childListArray); |
michael@0 | 8429 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 8430 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 8431 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 8432 | RemoveInPopupStateBitFromDescendants(childFrames.get()); |
michael@0 | 8433 | } |
michael@0 | 8434 | } |
michael@0 | 8435 | } |
michael@0 | 8436 | |
michael@0 | 8437 | void |
michael@0 | 8438 | nsFrame::SetParent(nsIFrame* aParent) |
michael@0 | 8439 | { |
michael@0 | 8440 | bool wasBoxWrapped = IsBoxWrapped(); |
michael@0 | 8441 | mParent = aParent; |
michael@0 | 8442 | if (!wasBoxWrapped && IsBoxWrapped()) { |
michael@0 | 8443 | InitBoxMetrics(true); |
michael@0 | 8444 | } else if (wasBoxWrapped && !IsBoxWrapped()) { |
michael@0 | 8445 | Properties().Delete(BoxMetricsProperty()); |
michael@0 | 8446 | } |
michael@0 | 8447 | |
michael@0 | 8448 | if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) { |
michael@0 | 8449 | for (nsIFrame* f = aParent; |
michael@0 | 8450 | f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW); |
michael@0 | 8451 | f = f->GetParent()) { |
michael@0 | 8452 | f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); |
michael@0 | 8453 | } |
michael@0 | 8454 | } |
michael@0 | 8455 | |
michael@0 | 8456 | if (HasInvalidFrameInSubtree()) { |
michael@0 | 8457 | for (nsIFrame* f = aParent; |
michael@0 | 8458 | f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
michael@0 | 8459 | f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
michael@0 | 8460 | f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
michael@0 | 8461 | } |
michael@0 | 8462 | } |
michael@0 | 8463 | |
michael@0 | 8464 | if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) { |
michael@0 | 8465 | AddInPopupStateBitToDescendants(this); |
michael@0 | 8466 | } else { |
michael@0 | 8467 | RemoveInPopupStateBitFromDescendants(this); |
michael@0 | 8468 | } |
michael@0 | 8469 | |
michael@0 | 8470 | // If our new parent only has invalid children, then we just invalidate |
michael@0 | 8471 | // ourselves too. This is probably faster than clearing the flag all |
michael@0 | 8472 | // the way up the frame tree. |
michael@0 | 8473 | if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) { |
michael@0 | 8474 | InvalidateFrame(); |
michael@0 | 8475 | } |
michael@0 | 8476 | } |
michael@0 | 8477 | |
michael@0 | 8478 | void |
michael@0 | 8479 | nsFrame::InitBoxMetrics(bool aClear) |
michael@0 | 8480 | { |
michael@0 | 8481 | FrameProperties props = Properties(); |
michael@0 | 8482 | if (aClear) { |
michael@0 | 8483 | props.Delete(BoxMetricsProperty()); |
michael@0 | 8484 | } |
michael@0 | 8485 | |
michael@0 | 8486 | nsBoxLayoutMetrics *metrics = new nsBoxLayoutMetrics(); |
michael@0 | 8487 | props.Set(BoxMetricsProperty(), metrics); |
michael@0 | 8488 | |
michael@0 | 8489 | nsFrame::MarkIntrinsicWidthsDirty(); |
michael@0 | 8490 | metrics->mBlockAscent = 0; |
michael@0 | 8491 | metrics->mLastSize.SizeTo(0, 0); |
michael@0 | 8492 | } |
michael@0 | 8493 | |
michael@0 | 8494 | void |
michael@0 | 8495 | nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, |
michael@0 | 8496 | nsDisplayList* aList) |
michael@0 | 8497 | { |
michael@0 | 8498 | if (GetContent() && |
michael@0 | 8499 | GetContent()->IsXUL() && |
michael@0 | 8500 | GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) { |
michael@0 | 8501 | aList->AppendNewToTop(new (aBuilder) |
michael@0 | 8502 | nsDisplayOwnLayer(aBuilder, this, aList)); |
michael@0 | 8503 | } |
michael@0 | 8504 | } |
michael@0 | 8505 | |
michael@0 | 8506 | bool |
michael@0 | 8507 | nsIFrame::IsSelected() const |
michael@0 | 8508 | { |
michael@0 | 8509 | return (GetContent() && GetContent()->IsSelectionDescendant()) ? |
michael@0 | 8510 | IsFrameSelected() : false; |
michael@0 | 8511 | } |
michael@0 | 8512 | |
michael@0 | 8513 | void |
michael@0 | 8514 | nsIFrame::DestroySurface(void* aPropertyValue) |
michael@0 | 8515 | { |
michael@0 | 8516 | static_cast<gfxASurface*>(aPropertyValue)->Release(); |
michael@0 | 8517 | } |
michael@0 | 8518 | |
michael@0 | 8519 | void |
michael@0 | 8520 | nsIFrame::DestroyDT(void* aPropertyValue) |
michael@0 | 8521 | { |
michael@0 | 8522 | static_cast<mozilla::gfx::DrawTarget*>(aPropertyValue)->Release(); |
michael@0 | 8523 | } |
michael@0 | 8524 | |
michael@0 | 8525 | void |
michael@0 | 8526 | nsIFrame::DestroyRegion(void* aPropertyValue) |
michael@0 | 8527 | { |
michael@0 | 8528 | delete static_cast<nsRegion*>(aPropertyValue); |
michael@0 | 8529 | } |
michael@0 | 8530 | |
michael@0 | 8531 | bool |
michael@0 | 8532 | nsIFrame::IsPseudoStackingContextFromStyle() { |
michael@0 | 8533 | const nsStyleDisplay* disp = StyleDisplay(); |
michael@0 | 8534 | // If you change this, also change the computation of pseudoStackingContext |
michael@0 | 8535 | // in BuildDisplayListForChild() |
michael@0 | 8536 | return disp->mOpacity != 1.0f || |
michael@0 | 8537 | disp->IsPositioned(this) || |
michael@0 | 8538 | disp->IsFloating(this) || |
michael@0 | 8539 | (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT); |
michael@0 | 8540 | } |
michael@0 | 8541 | |
michael@0 | 8542 | Element* |
michael@0 | 8543 | nsIFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) |
michael@0 | 8544 | { |
michael@0 | 8545 | nsIFrame* frame = nullptr; |
michael@0 | 8546 | |
michael@0 | 8547 | if (aType == nsCSSPseudoElements::ePseudo_before) { |
michael@0 | 8548 | frame = nsLayoutUtils::GetBeforeFrame(this); |
michael@0 | 8549 | } else if (aType == nsCSSPseudoElements::ePseudo_after) { |
michael@0 | 8550 | frame = nsLayoutUtils::GetAfterFrame(this); |
michael@0 | 8551 | } |
michael@0 | 8552 | |
michael@0 | 8553 | if (frame) { |
michael@0 | 8554 | nsIContent* content = frame->GetContent(); |
michael@0 | 8555 | if (content->IsElement()) { |
michael@0 | 8556 | return content->AsElement(); |
michael@0 | 8557 | } |
michael@0 | 8558 | } |
michael@0 | 8559 | |
michael@0 | 8560 | return nullptr; |
michael@0 | 8561 | } |
michael@0 | 8562 | |
michael@0 | 8563 | nsIFrame::ContentOffsets::ContentOffsets() |
michael@0 | 8564 | { |
michael@0 | 8565 | } |
michael@0 | 8566 | |
michael@0 | 8567 | nsIFrame::ContentOffsets::ContentOffsets(const ContentOffsets& rhs) |
michael@0 | 8568 | : content(rhs.content), |
michael@0 | 8569 | offset(rhs.offset), |
michael@0 | 8570 | secondaryOffset(rhs.secondaryOffset), |
michael@0 | 8571 | associateWithNext(rhs.associateWithNext) |
michael@0 | 8572 | { |
michael@0 | 8573 | } |
michael@0 | 8574 | |
michael@0 | 8575 | nsIFrame::ContentOffsets::~ContentOffsets() |
michael@0 | 8576 | { |
michael@0 | 8577 | } |
michael@0 | 8578 | |
michael@0 | 8579 | nsIFrame::CaretPosition::CaretPosition() |
michael@0 | 8580 | : mContentOffset(0) |
michael@0 | 8581 | { |
michael@0 | 8582 | } |
michael@0 | 8583 | |
michael@0 | 8584 | nsIFrame::CaretPosition::~CaretPosition() |
michael@0 | 8585 | { |
michael@0 | 8586 | } |
michael@0 | 8587 | |
michael@0 | 8588 | // Box layout debugging |
michael@0 | 8589 | #ifdef DEBUG_REFLOW |
michael@0 | 8590 | int32_t gIndent2 = 0; |
michael@0 | 8591 | |
michael@0 | 8592 | void |
michael@0 | 8593 | nsAdaptorAddIndents() |
michael@0 | 8594 | { |
michael@0 | 8595 | for(int32_t i=0; i < gIndent2; i++) |
michael@0 | 8596 | { |
michael@0 | 8597 | printf(" "); |
michael@0 | 8598 | } |
michael@0 | 8599 | } |
michael@0 | 8600 | |
michael@0 | 8601 | void |
michael@0 | 8602 | nsAdaptorPrintReason(nsHTMLReflowState& aReflowState) |
michael@0 | 8603 | { |
michael@0 | 8604 | char* reflowReasonString; |
michael@0 | 8605 | |
michael@0 | 8606 | switch(aReflowState.reason) |
michael@0 | 8607 | { |
michael@0 | 8608 | case eReflowReason_Initial: |
michael@0 | 8609 | reflowReasonString = "initial"; |
michael@0 | 8610 | break; |
michael@0 | 8611 | |
michael@0 | 8612 | case eReflowReason_Resize: |
michael@0 | 8613 | reflowReasonString = "resize"; |
michael@0 | 8614 | break; |
michael@0 | 8615 | case eReflowReason_Dirty: |
michael@0 | 8616 | reflowReasonString = "dirty"; |
michael@0 | 8617 | break; |
michael@0 | 8618 | case eReflowReason_StyleChange: |
michael@0 | 8619 | reflowReasonString = "stylechange"; |
michael@0 | 8620 | break; |
michael@0 | 8621 | case eReflowReason_Incremental: |
michael@0 | 8622 | { |
michael@0 | 8623 | switch (aReflowState.reflowCommand->Type()) { |
michael@0 | 8624 | case eReflowType_StyleChanged: |
michael@0 | 8625 | reflowReasonString = "incremental (StyleChanged)"; |
michael@0 | 8626 | break; |
michael@0 | 8627 | case eReflowType_ReflowDirty: |
michael@0 | 8628 | reflowReasonString = "incremental (ReflowDirty)"; |
michael@0 | 8629 | break; |
michael@0 | 8630 | default: |
michael@0 | 8631 | reflowReasonString = "incremental (Unknown)"; |
michael@0 | 8632 | } |
michael@0 | 8633 | } |
michael@0 | 8634 | break; |
michael@0 | 8635 | default: |
michael@0 | 8636 | reflowReasonString = "unknown"; |
michael@0 | 8637 | break; |
michael@0 | 8638 | } |
michael@0 | 8639 | |
michael@0 | 8640 | printf("%s",reflowReasonString); |
michael@0 | 8641 | } |
michael@0 | 8642 | |
michael@0 | 8643 | #endif |
michael@0 | 8644 | #ifdef DEBUG_LAYOUT |
michael@0 | 8645 | void |
michael@0 | 8646 | nsFrame::GetBoxName(nsAutoString& aName) |
michael@0 | 8647 | { |
michael@0 | 8648 | GetFrameName(aName); |
michael@0 | 8649 | } |
michael@0 | 8650 | #endif |
michael@0 | 8651 | |
michael@0 | 8652 | #ifdef DEBUG |
michael@0 | 8653 | static void |
michael@0 | 8654 | GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize, |
michael@0 | 8655 | char* aResult) |
michael@0 | 8656 | { |
michael@0 | 8657 | if (aContent) { |
michael@0 | 8658 | PR_snprintf(aResult, aResultSize, "%s@%p", |
michael@0 | 8659 | nsAtomCString(aContent->Tag()).get(), aFrame); |
michael@0 | 8660 | } |
michael@0 | 8661 | else { |
michael@0 | 8662 | PR_snprintf(aResult, aResultSize, "@%p", aFrame); |
michael@0 | 8663 | } |
michael@0 | 8664 | } |
michael@0 | 8665 | |
michael@0 | 8666 | void |
michael@0 | 8667 | nsFrame::Trace(const char* aMethod, bool aEnter) |
michael@0 | 8668 | { |
michael@0 | 8669 | if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { |
michael@0 | 8670 | char tagbuf[40]; |
michael@0 | 8671 | GetTagName(this, mContent, sizeof(tagbuf), tagbuf); |
michael@0 | 8672 | PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod); |
michael@0 | 8673 | } |
michael@0 | 8674 | } |
michael@0 | 8675 | |
michael@0 | 8676 | void |
michael@0 | 8677 | nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus) |
michael@0 | 8678 | { |
michael@0 | 8679 | if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { |
michael@0 | 8680 | char tagbuf[40]; |
michael@0 | 8681 | GetTagName(this, mContent, sizeof(tagbuf), tagbuf); |
michael@0 | 8682 | PR_LogPrint("%s: %s %s, status=%scomplete%s", |
michael@0 | 8683 | tagbuf, aEnter ? "enter" : "exit", aMethod, |
michael@0 | 8684 | NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "", |
michael@0 | 8685 | (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : ""); |
michael@0 | 8686 | } |
michael@0 | 8687 | } |
michael@0 | 8688 | |
michael@0 | 8689 | void |
michael@0 | 8690 | nsFrame::TraceMsg(const char* aFormatString, ...) |
michael@0 | 8691 | { |
michael@0 | 8692 | if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { |
michael@0 | 8693 | // Format arguments into a buffer |
michael@0 | 8694 | char argbuf[200]; |
michael@0 | 8695 | va_list ap; |
michael@0 | 8696 | va_start(ap, aFormatString); |
michael@0 | 8697 | PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap); |
michael@0 | 8698 | va_end(ap); |
michael@0 | 8699 | |
michael@0 | 8700 | char tagbuf[40]; |
michael@0 | 8701 | GetTagName(this, mContent, sizeof(tagbuf), tagbuf); |
michael@0 | 8702 | PR_LogPrint("%s: %s", tagbuf, argbuf); |
michael@0 | 8703 | } |
michael@0 | 8704 | } |
michael@0 | 8705 | |
michael@0 | 8706 | void |
michael@0 | 8707 | nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) |
michael@0 | 8708 | { |
michael@0 | 8709 | for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { |
michael@0 | 8710 | NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY, |
michael@0 | 8711 | "dirty bit not set"); |
michael@0 | 8712 | } |
michael@0 | 8713 | } |
michael@0 | 8714 | |
michael@0 | 8715 | // Start Display Reflow |
michael@0 | 8716 | #ifdef DEBUG |
michael@0 | 8717 | |
michael@0 | 8718 | DR_cookie::DR_cookie(nsPresContext* aPresContext, |
michael@0 | 8719 | nsIFrame* aFrame, |
michael@0 | 8720 | const nsHTMLReflowState& aReflowState, |
michael@0 | 8721 | nsHTMLReflowMetrics& aMetrics, |
michael@0 | 8722 | nsReflowStatus& aStatus) |
michael@0 | 8723 | :mPresContext(aPresContext), mFrame(aFrame), mReflowState(aReflowState), mMetrics(aMetrics), mStatus(aStatus) |
michael@0 | 8724 | { |
michael@0 | 8725 | MOZ_COUNT_CTOR(DR_cookie); |
michael@0 | 8726 | mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowState); |
michael@0 | 8727 | } |
michael@0 | 8728 | |
michael@0 | 8729 | DR_cookie::~DR_cookie() |
michael@0 | 8730 | { |
michael@0 | 8731 | MOZ_COUNT_DTOR(DR_cookie); |
michael@0 | 8732 | nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue); |
michael@0 | 8733 | } |
michael@0 | 8734 | |
michael@0 | 8735 | DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) |
michael@0 | 8736 | : mFrame(aFrame) |
michael@0 | 8737 | { |
michael@0 | 8738 | MOZ_COUNT_CTOR(DR_layout_cookie); |
michael@0 | 8739 | mValue = nsFrame::DisplayLayoutEnter(mFrame); |
michael@0 | 8740 | } |
michael@0 | 8741 | |
michael@0 | 8742 | DR_layout_cookie::~DR_layout_cookie() |
michael@0 | 8743 | { |
michael@0 | 8744 | MOZ_COUNT_DTOR(DR_layout_cookie); |
michael@0 | 8745 | nsFrame::DisplayLayoutExit(mFrame, mValue); |
michael@0 | 8746 | } |
michael@0 | 8747 | |
michael@0 | 8748 | DR_intrinsic_width_cookie::DR_intrinsic_width_cookie( |
michael@0 | 8749 | nsIFrame* aFrame, |
michael@0 | 8750 | const char* aType, |
michael@0 | 8751 | nscoord& aResult) |
michael@0 | 8752 | : mFrame(aFrame) |
michael@0 | 8753 | , mType(aType) |
michael@0 | 8754 | , mResult(aResult) |
michael@0 | 8755 | { |
michael@0 | 8756 | MOZ_COUNT_CTOR(DR_intrinsic_width_cookie); |
michael@0 | 8757 | mValue = nsFrame::DisplayIntrinsicWidthEnter(mFrame, mType); |
michael@0 | 8758 | } |
michael@0 | 8759 | |
michael@0 | 8760 | DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie() |
michael@0 | 8761 | { |
michael@0 | 8762 | MOZ_COUNT_DTOR(DR_intrinsic_width_cookie); |
michael@0 | 8763 | nsFrame::DisplayIntrinsicWidthExit(mFrame, mType, mResult, mValue); |
michael@0 | 8764 | } |
michael@0 | 8765 | |
michael@0 | 8766 | DR_intrinsic_size_cookie::DR_intrinsic_size_cookie( |
michael@0 | 8767 | nsIFrame* aFrame, |
michael@0 | 8768 | const char* aType, |
michael@0 | 8769 | nsSize& aResult) |
michael@0 | 8770 | : mFrame(aFrame) |
michael@0 | 8771 | , mType(aType) |
michael@0 | 8772 | , mResult(aResult) |
michael@0 | 8773 | { |
michael@0 | 8774 | MOZ_COUNT_CTOR(DR_intrinsic_size_cookie); |
michael@0 | 8775 | mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType); |
michael@0 | 8776 | } |
michael@0 | 8777 | |
michael@0 | 8778 | DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() |
michael@0 | 8779 | { |
michael@0 | 8780 | MOZ_COUNT_DTOR(DR_intrinsic_size_cookie); |
michael@0 | 8781 | nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue); |
michael@0 | 8782 | } |
michael@0 | 8783 | |
michael@0 | 8784 | DR_init_constraints_cookie::DR_init_constraints_cookie( |
michael@0 | 8785 | nsIFrame* aFrame, |
michael@0 | 8786 | nsHTMLReflowState* aState, |
michael@0 | 8787 | nscoord aCBWidth, |
michael@0 | 8788 | nscoord aCBHeight, |
michael@0 | 8789 | const nsMargin* aMargin, |
michael@0 | 8790 | const nsMargin* aPadding) |
michael@0 | 8791 | : mFrame(aFrame) |
michael@0 | 8792 | , mState(aState) |
michael@0 | 8793 | { |
michael@0 | 8794 | MOZ_COUNT_CTOR(DR_init_constraints_cookie); |
michael@0 | 8795 | mValue = nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame, mState, |
michael@0 | 8796 | aCBWidth, aCBHeight, |
michael@0 | 8797 | aMargin, aPadding); |
michael@0 | 8798 | } |
michael@0 | 8799 | |
michael@0 | 8800 | DR_init_constraints_cookie::~DR_init_constraints_cookie() |
michael@0 | 8801 | { |
michael@0 | 8802 | MOZ_COUNT_DTOR(DR_init_constraints_cookie); |
michael@0 | 8803 | nsHTMLReflowState::DisplayInitConstraintsExit(mFrame, mState, mValue); |
michael@0 | 8804 | } |
michael@0 | 8805 | |
michael@0 | 8806 | DR_init_offsets_cookie::DR_init_offsets_cookie( |
michael@0 | 8807 | nsIFrame* aFrame, |
michael@0 | 8808 | nsCSSOffsetState* aState, |
michael@0 | 8809 | nscoord aHorizontalPercentBasis, |
michael@0 | 8810 | nscoord aVerticalPercentBasis, |
michael@0 | 8811 | const nsMargin* aMargin, |
michael@0 | 8812 | const nsMargin* aPadding) |
michael@0 | 8813 | : mFrame(aFrame) |
michael@0 | 8814 | , mState(aState) |
michael@0 | 8815 | { |
michael@0 | 8816 | MOZ_COUNT_CTOR(DR_init_offsets_cookie); |
michael@0 | 8817 | mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState, |
michael@0 | 8818 | aHorizontalPercentBasis, |
michael@0 | 8819 | aVerticalPercentBasis, |
michael@0 | 8820 | aMargin, aPadding); |
michael@0 | 8821 | } |
michael@0 | 8822 | |
michael@0 | 8823 | DR_init_offsets_cookie::~DR_init_offsets_cookie() |
michael@0 | 8824 | { |
michael@0 | 8825 | MOZ_COUNT_DTOR(DR_init_offsets_cookie); |
michael@0 | 8826 | nsCSSOffsetState::DisplayInitOffsetsExit(mFrame, mState, mValue); |
michael@0 | 8827 | } |
michael@0 | 8828 | |
michael@0 | 8829 | DR_init_type_cookie::DR_init_type_cookie( |
michael@0 | 8830 | nsIFrame* aFrame, |
michael@0 | 8831 | nsHTMLReflowState* aState) |
michael@0 | 8832 | : mFrame(aFrame) |
michael@0 | 8833 | , mState(aState) |
michael@0 | 8834 | { |
michael@0 | 8835 | MOZ_COUNT_CTOR(DR_init_type_cookie); |
michael@0 | 8836 | mValue = nsHTMLReflowState::DisplayInitFrameTypeEnter(mFrame, mState); |
michael@0 | 8837 | } |
michael@0 | 8838 | |
michael@0 | 8839 | DR_init_type_cookie::~DR_init_type_cookie() |
michael@0 | 8840 | { |
michael@0 | 8841 | MOZ_COUNT_DTOR(DR_init_type_cookie); |
michael@0 | 8842 | nsHTMLReflowState::DisplayInitFrameTypeExit(mFrame, mState, mValue); |
michael@0 | 8843 | } |
michael@0 | 8844 | |
michael@0 | 8845 | struct DR_FrameTypeInfo; |
michael@0 | 8846 | struct DR_FrameTreeNode; |
michael@0 | 8847 | struct DR_Rule; |
michael@0 | 8848 | |
michael@0 | 8849 | struct DR_State |
michael@0 | 8850 | { |
michael@0 | 8851 | DR_State(); |
michael@0 | 8852 | ~DR_State(); |
michael@0 | 8853 | void Init(); |
michael@0 | 8854 | void AddFrameTypeInfo(nsIAtom* aFrameType, |
michael@0 | 8855 | const char* aFrameNameAbbrev, |
michael@0 | 8856 | const char* aFrameName); |
michael@0 | 8857 | DR_FrameTypeInfo* GetFrameTypeInfo(nsIAtom* aFrameType); |
michael@0 | 8858 | DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName); |
michael@0 | 8859 | void InitFrameTypeTable(); |
michael@0 | 8860 | DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame, |
michael@0 | 8861 | const nsHTMLReflowState* aReflowState); |
michael@0 | 8862 | void FindMatchingRule(DR_FrameTreeNode& aNode); |
michael@0 | 8863 | bool RuleMatches(DR_Rule& aRule, |
michael@0 | 8864 | DR_FrameTreeNode& aNode); |
michael@0 | 8865 | bool GetToken(FILE* aFile, |
michael@0 | 8866 | char* aBuf, |
michael@0 | 8867 | size_t aBufSize); |
michael@0 | 8868 | DR_Rule* ParseRule(FILE* aFile); |
michael@0 | 8869 | void ParseRulesFile(); |
michael@0 | 8870 | void AddRule(nsTArray<DR_Rule*>& aRules, |
michael@0 | 8871 | DR_Rule& aRule); |
michael@0 | 8872 | bool IsWhiteSpace(int c); |
michael@0 | 8873 | bool GetNumber(char* aBuf, |
michael@0 | 8874 | int32_t& aNumber); |
michael@0 | 8875 | void PrettyUC(nscoord aSize, |
michael@0 | 8876 | char* aBuf); |
michael@0 | 8877 | void PrintMargin(const char* tag, const nsMargin* aMargin); |
michael@0 | 8878 | void DisplayFrameTypeInfo(nsIFrame* aFrame, |
michael@0 | 8879 | int32_t aIndent); |
michael@0 | 8880 | void DeleteTreeNode(DR_FrameTreeNode& aNode); |
michael@0 | 8881 | |
michael@0 | 8882 | bool mInited; |
michael@0 | 8883 | bool mActive; |
michael@0 | 8884 | int32_t mCount; |
michael@0 | 8885 | int32_t mAssert; |
michael@0 | 8886 | int32_t mIndent; |
michael@0 | 8887 | bool mIndentUndisplayedFrames; |
michael@0 | 8888 | bool mDisplayPixelErrors; |
michael@0 | 8889 | nsTArray<DR_Rule*> mWildRules; |
michael@0 | 8890 | nsTArray<DR_FrameTypeInfo> mFrameTypeTable; |
michael@0 | 8891 | // reflow specific state |
michael@0 | 8892 | nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves; |
michael@0 | 8893 | }; |
michael@0 | 8894 | |
michael@0 | 8895 | static DR_State *DR_state; // the one and only DR_State |
michael@0 | 8896 | |
michael@0 | 8897 | struct DR_RulePart |
michael@0 | 8898 | { |
michael@0 | 8899 | DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {} |
michael@0 | 8900 | void Destroy(); |
michael@0 | 8901 | |
michael@0 | 8902 | nsIAtom* mFrameType; |
michael@0 | 8903 | DR_RulePart* mNext; |
michael@0 | 8904 | }; |
michael@0 | 8905 | |
michael@0 | 8906 | void DR_RulePart::Destroy() |
michael@0 | 8907 | { |
michael@0 | 8908 | if (mNext) { |
michael@0 | 8909 | mNext->Destroy(); |
michael@0 | 8910 | } |
michael@0 | 8911 | delete this; |
michael@0 | 8912 | } |
michael@0 | 8913 | |
michael@0 | 8914 | struct DR_Rule |
michael@0 | 8915 | { |
michael@0 | 8916 | DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) { |
michael@0 | 8917 | MOZ_COUNT_CTOR(DR_Rule); |
michael@0 | 8918 | } |
michael@0 | 8919 | ~DR_Rule() { |
michael@0 | 8920 | if (mTarget) mTarget->Destroy(); |
michael@0 | 8921 | MOZ_COUNT_DTOR(DR_Rule); |
michael@0 | 8922 | } |
michael@0 | 8923 | void AddPart(nsIAtom* aFrameType); |
michael@0 | 8924 | |
michael@0 | 8925 | uint32_t mLength; |
michael@0 | 8926 | DR_RulePart* mTarget; |
michael@0 | 8927 | bool mDisplay; |
michael@0 | 8928 | }; |
michael@0 | 8929 | |
michael@0 | 8930 | void DR_Rule::AddPart(nsIAtom* aFrameType) |
michael@0 | 8931 | { |
michael@0 | 8932 | DR_RulePart* newPart = new DR_RulePart(aFrameType); |
michael@0 | 8933 | newPart->mNext = mTarget; |
michael@0 | 8934 | mTarget = newPart; |
michael@0 | 8935 | mLength++; |
michael@0 | 8936 | } |
michael@0 | 8937 | |
michael@0 | 8938 | struct DR_FrameTypeInfo |
michael@0 | 8939 | { |
michael@0 | 8940 | DR_FrameTypeInfo(nsIAtom* aFrmeType, const char* aFrameNameAbbrev, const char* aFrameName); |
michael@0 | 8941 | ~DR_FrameTypeInfo() { |
michael@0 | 8942 | int32_t numElements; |
michael@0 | 8943 | numElements = mRules.Length(); |
michael@0 | 8944 | for (int32_t i = numElements - 1; i >= 0; i--) { |
michael@0 | 8945 | delete mRules.ElementAt(i); |
michael@0 | 8946 | } |
michael@0 | 8947 | } |
michael@0 | 8948 | |
michael@0 | 8949 | nsIAtom* mType; |
michael@0 | 8950 | char mNameAbbrev[16]; |
michael@0 | 8951 | char mName[32]; |
michael@0 | 8952 | nsTArray<DR_Rule*> mRules; |
michael@0 | 8953 | private: |
michael@0 | 8954 | DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) MOZ_DELETE; |
michael@0 | 8955 | }; |
michael@0 | 8956 | |
michael@0 | 8957 | DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType, |
michael@0 | 8958 | const char* aFrameNameAbbrev, |
michael@0 | 8959 | const char* aFrameName) |
michael@0 | 8960 | { |
michael@0 | 8961 | mType = aFrameType; |
michael@0 | 8962 | PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev)); |
michael@0 | 8963 | PL_strncpyz(mName, aFrameName, sizeof(mName)); |
michael@0 | 8964 | } |
michael@0 | 8965 | |
michael@0 | 8966 | struct DR_FrameTreeNode |
michael@0 | 8967 | { |
michael@0 | 8968 | DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) |
michael@0 | 8969 | { |
michael@0 | 8970 | MOZ_COUNT_CTOR(DR_FrameTreeNode); |
michael@0 | 8971 | } |
michael@0 | 8972 | |
michael@0 | 8973 | ~DR_FrameTreeNode() |
michael@0 | 8974 | { |
michael@0 | 8975 | MOZ_COUNT_DTOR(DR_FrameTreeNode); |
michael@0 | 8976 | } |
michael@0 | 8977 | |
michael@0 | 8978 | nsIFrame* mFrame; |
michael@0 | 8979 | DR_FrameTreeNode* mParent; |
michael@0 | 8980 | bool mDisplay; |
michael@0 | 8981 | uint32_t mIndent; |
michael@0 | 8982 | }; |
michael@0 | 8983 | |
michael@0 | 8984 | // DR_State implementation |
michael@0 | 8985 | |
michael@0 | 8986 | DR_State::DR_State() |
michael@0 | 8987 | : mInited(false), mActive(false), mCount(0), mAssert(-1), mIndent(0), |
michael@0 | 8988 | mIndentUndisplayedFrames(false), mDisplayPixelErrors(false) |
michael@0 | 8989 | { |
michael@0 | 8990 | MOZ_COUNT_CTOR(DR_State); |
michael@0 | 8991 | } |
michael@0 | 8992 | |
michael@0 | 8993 | void DR_State::Init() |
michael@0 | 8994 | { |
michael@0 | 8995 | char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT"); |
michael@0 | 8996 | int32_t num; |
michael@0 | 8997 | if (env) { |
michael@0 | 8998 | if (GetNumber(env, num)) |
michael@0 | 8999 | mAssert = num; |
michael@0 | 9000 | else |
michael@0 | 9001 | printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env); |
michael@0 | 9002 | } |
michael@0 | 9003 | |
michael@0 | 9004 | env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START"); |
michael@0 | 9005 | if (env) { |
michael@0 | 9006 | if (GetNumber(env, num)) |
michael@0 | 9007 | mIndent = num; |
michael@0 | 9008 | else |
michael@0 | 9009 | printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env); |
michael@0 | 9010 | } |
michael@0 | 9011 | |
michael@0 | 9012 | env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES"); |
michael@0 | 9013 | if (env) { |
michael@0 | 9014 | if (GetNumber(env, num)) |
michael@0 | 9015 | mIndentUndisplayedFrames = num; |
michael@0 | 9016 | else |
michael@0 | 9017 | printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env); |
michael@0 | 9018 | } |
michael@0 | 9019 | |
michael@0 | 9020 | env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS"); |
michael@0 | 9021 | if (env) { |
michael@0 | 9022 | if (GetNumber(env, num)) |
michael@0 | 9023 | mDisplayPixelErrors = num; |
michael@0 | 9024 | else |
michael@0 | 9025 | printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env); |
michael@0 | 9026 | } |
michael@0 | 9027 | |
michael@0 | 9028 | InitFrameTypeTable(); |
michael@0 | 9029 | ParseRulesFile(); |
michael@0 | 9030 | mInited = true; |
michael@0 | 9031 | } |
michael@0 | 9032 | |
michael@0 | 9033 | DR_State::~DR_State() |
michael@0 | 9034 | { |
michael@0 | 9035 | MOZ_COUNT_DTOR(DR_State); |
michael@0 | 9036 | int32_t numElements, i; |
michael@0 | 9037 | numElements = mWildRules.Length(); |
michael@0 | 9038 | for (i = numElements - 1; i >= 0; i--) { |
michael@0 | 9039 | delete mWildRules.ElementAt(i); |
michael@0 | 9040 | } |
michael@0 | 9041 | numElements = mFrameTreeLeaves.Length(); |
michael@0 | 9042 | for (i = numElements - 1; i >= 0; i--) { |
michael@0 | 9043 | delete mFrameTreeLeaves.ElementAt(i); |
michael@0 | 9044 | } |
michael@0 | 9045 | } |
michael@0 | 9046 | |
michael@0 | 9047 | bool DR_State::GetNumber(char* aBuf, |
michael@0 | 9048 | int32_t& aNumber) |
michael@0 | 9049 | { |
michael@0 | 9050 | if (sscanf(aBuf, "%d", &aNumber) > 0) |
michael@0 | 9051 | return true; |
michael@0 | 9052 | else |
michael@0 | 9053 | return false; |
michael@0 | 9054 | } |
michael@0 | 9055 | |
michael@0 | 9056 | bool DR_State::IsWhiteSpace(int c) { |
michael@0 | 9057 | return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); |
michael@0 | 9058 | } |
michael@0 | 9059 | |
michael@0 | 9060 | bool DR_State::GetToken(FILE* aFile, |
michael@0 | 9061 | char* aBuf, |
michael@0 | 9062 | size_t aBufSize) |
michael@0 | 9063 | { |
michael@0 | 9064 | bool haveToken = false; |
michael@0 | 9065 | aBuf[0] = 0; |
michael@0 | 9066 | // get the 1st non whitespace char |
michael@0 | 9067 | int c = -1; |
michael@0 | 9068 | for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) { |
michael@0 | 9069 | } |
michael@0 | 9070 | |
michael@0 | 9071 | if (c > 0) { |
michael@0 | 9072 | haveToken = true; |
michael@0 | 9073 | aBuf[0] = c; |
michael@0 | 9074 | // get everything up to the next whitespace char |
michael@0 | 9075 | size_t cX; |
michael@0 | 9076 | for (cX = 1; cX + 1 < aBufSize ; cX++) { |
michael@0 | 9077 | c = getc(aFile); |
michael@0 | 9078 | if (c < 0) { // EOF |
michael@0 | 9079 | ungetc(' ', aFile); |
michael@0 | 9080 | break; |
michael@0 | 9081 | } |
michael@0 | 9082 | else { |
michael@0 | 9083 | if (IsWhiteSpace(c)) { |
michael@0 | 9084 | break; |
michael@0 | 9085 | } |
michael@0 | 9086 | else { |
michael@0 | 9087 | aBuf[cX] = c; |
michael@0 | 9088 | } |
michael@0 | 9089 | } |
michael@0 | 9090 | } |
michael@0 | 9091 | aBuf[cX] = 0; |
michael@0 | 9092 | } |
michael@0 | 9093 | return haveToken; |
michael@0 | 9094 | } |
michael@0 | 9095 | |
michael@0 | 9096 | DR_Rule* DR_State::ParseRule(FILE* aFile) |
michael@0 | 9097 | { |
michael@0 | 9098 | char buf[128]; |
michael@0 | 9099 | int32_t doDisplay; |
michael@0 | 9100 | DR_Rule* rule = nullptr; |
michael@0 | 9101 | while (GetToken(aFile, buf, sizeof(buf))) { |
michael@0 | 9102 | if (GetNumber(buf, doDisplay)) { |
michael@0 | 9103 | if (rule) { |
michael@0 | 9104 | rule->mDisplay = !!doDisplay; |
michael@0 | 9105 | break; |
michael@0 | 9106 | } |
michael@0 | 9107 | else { |
michael@0 | 9108 | printf("unexpected token - %s \n", buf); |
michael@0 | 9109 | } |
michael@0 | 9110 | } |
michael@0 | 9111 | else { |
michael@0 | 9112 | if (!rule) { |
michael@0 | 9113 | rule = new DR_Rule; |
michael@0 | 9114 | } |
michael@0 | 9115 | if (strcmp(buf, "*") == 0) { |
michael@0 | 9116 | rule->AddPart(nullptr); |
michael@0 | 9117 | } |
michael@0 | 9118 | else { |
michael@0 | 9119 | DR_FrameTypeInfo* info = GetFrameTypeInfo(buf); |
michael@0 | 9120 | if (info) { |
michael@0 | 9121 | rule->AddPart(info->mType); |
michael@0 | 9122 | } |
michael@0 | 9123 | else { |
michael@0 | 9124 | printf("invalid frame type - %s \n", buf); |
michael@0 | 9125 | } |
michael@0 | 9126 | } |
michael@0 | 9127 | } |
michael@0 | 9128 | } |
michael@0 | 9129 | return rule; |
michael@0 | 9130 | } |
michael@0 | 9131 | |
michael@0 | 9132 | void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, |
michael@0 | 9133 | DR_Rule& aRule) |
michael@0 | 9134 | { |
michael@0 | 9135 | int32_t numRules = aRules.Length(); |
michael@0 | 9136 | for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { |
michael@0 | 9137 | DR_Rule* rule = aRules.ElementAt(ruleX); |
michael@0 | 9138 | NS_ASSERTION(rule, "program error"); |
michael@0 | 9139 | if (aRule.mLength > rule->mLength) { |
michael@0 | 9140 | aRules.InsertElementAt(ruleX, &aRule); |
michael@0 | 9141 | return; |
michael@0 | 9142 | } |
michael@0 | 9143 | } |
michael@0 | 9144 | aRules.AppendElement(&aRule); |
michael@0 | 9145 | } |
michael@0 | 9146 | |
michael@0 | 9147 | void DR_State::ParseRulesFile() |
michael@0 | 9148 | { |
michael@0 | 9149 | char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE"); |
michael@0 | 9150 | if (path) { |
michael@0 | 9151 | FILE* inFile = fopen(path, "r"); |
michael@0 | 9152 | if (inFile) { |
michael@0 | 9153 | for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) { |
michael@0 | 9154 | if (rule->mTarget) { |
michael@0 | 9155 | nsIAtom* fType = rule->mTarget->mFrameType; |
michael@0 | 9156 | if (fType) { |
michael@0 | 9157 | DR_FrameTypeInfo* info = GetFrameTypeInfo(fType); |
michael@0 | 9158 | if (info) { |
michael@0 | 9159 | AddRule(info->mRules, *rule); |
michael@0 | 9160 | } |
michael@0 | 9161 | } |
michael@0 | 9162 | else { |
michael@0 | 9163 | AddRule(mWildRules, *rule); |
michael@0 | 9164 | } |
michael@0 | 9165 | mActive = true; |
michael@0 | 9166 | } |
michael@0 | 9167 | } |
michael@0 | 9168 | } |
michael@0 | 9169 | } |
michael@0 | 9170 | } |
michael@0 | 9171 | |
michael@0 | 9172 | |
michael@0 | 9173 | void DR_State::AddFrameTypeInfo(nsIAtom* aFrameType, |
michael@0 | 9174 | const char* aFrameNameAbbrev, |
michael@0 | 9175 | const char* aFrameName) |
michael@0 | 9176 | { |
michael@0 | 9177 | mFrameTypeTable.AppendElement(DR_FrameTypeInfo(aFrameType, aFrameNameAbbrev, aFrameName)); |
michael@0 | 9178 | } |
michael@0 | 9179 | |
michael@0 | 9180 | DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(nsIAtom* aFrameType) |
michael@0 | 9181 | { |
michael@0 | 9182 | int32_t numEntries = mFrameTypeTable.Length(); |
michael@0 | 9183 | NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); |
michael@0 | 9184 | for (int32_t i = 0; i < numEntries; i++) { |
michael@0 | 9185 | DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); |
michael@0 | 9186 | if (info.mType == aFrameType) { |
michael@0 | 9187 | return &info; |
michael@0 | 9188 | } |
michael@0 | 9189 | } |
michael@0 | 9190 | return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type |
michael@0 | 9191 | } |
michael@0 | 9192 | |
michael@0 | 9193 | DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) |
michael@0 | 9194 | { |
michael@0 | 9195 | int32_t numEntries = mFrameTypeTable.Length(); |
michael@0 | 9196 | NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); |
michael@0 | 9197 | for (int32_t i = 0; i < numEntries; i++) { |
michael@0 | 9198 | DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); |
michael@0 | 9199 | if ((strcmp(aFrameName, info.mName) == 0) || (strcmp(aFrameName, info.mNameAbbrev) == 0)) { |
michael@0 | 9200 | return &info; |
michael@0 | 9201 | } |
michael@0 | 9202 | } |
michael@0 | 9203 | return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type |
michael@0 | 9204 | } |
michael@0 | 9205 | |
michael@0 | 9206 | void DR_State::InitFrameTypeTable() |
michael@0 | 9207 | { |
michael@0 | 9208 | AddFrameTypeInfo(nsGkAtoms::blockFrame, "block", "block"); |
michael@0 | 9209 | AddFrameTypeInfo(nsGkAtoms::brFrame, "br", "br"); |
michael@0 | 9210 | AddFrameTypeInfo(nsGkAtoms::bulletFrame, "bullet", "bullet"); |
michael@0 | 9211 | AddFrameTypeInfo(nsGkAtoms::colorControlFrame, "color", "colorControl"); |
michael@0 | 9212 | AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame, "button", "gfxButtonControl"); |
michael@0 | 9213 | AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame, "HTMLbutton", "HTMLButtonControl"); |
michael@0 | 9214 | AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame, "HTMLCanvas","HTMLCanvas"); |
michael@0 | 9215 | AddFrameTypeInfo(nsGkAtoms::subDocumentFrame, "subdoc", "subDocument"); |
michael@0 | 9216 | AddFrameTypeInfo(nsGkAtoms::imageFrame, "img", "image"); |
michael@0 | 9217 | AddFrameTypeInfo(nsGkAtoms::inlineFrame, "inline", "inline"); |
michael@0 | 9218 | AddFrameTypeInfo(nsGkAtoms::letterFrame, "letter", "letter"); |
michael@0 | 9219 | AddFrameTypeInfo(nsGkAtoms::lineFrame, "line", "line"); |
michael@0 | 9220 | AddFrameTypeInfo(nsGkAtoms::listControlFrame, "select", "select"); |
michael@0 | 9221 | AddFrameTypeInfo(nsGkAtoms::objectFrame, "obj", "object"); |
michael@0 | 9222 | AddFrameTypeInfo(nsGkAtoms::pageFrame, "page", "page"); |
michael@0 | 9223 | AddFrameTypeInfo(nsGkAtoms::placeholderFrame, "place", "placeholder"); |
michael@0 | 9224 | AddFrameTypeInfo(nsGkAtoms::canvasFrame, "canvas", "canvas"); |
michael@0 | 9225 | AddFrameTypeInfo(nsGkAtoms::rootFrame, "root", "root"); |
michael@0 | 9226 | AddFrameTypeInfo(nsGkAtoms::scrollFrame, "scroll", "scroll"); |
michael@0 | 9227 | AddFrameTypeInfo(nsGkAtoms::tableCaptionFrame, "caption", "tableCaption"); |
michael@0 | 9228 | AddFrameTypeInfo(nsGkAtoms::tableCellFrame, "cell", "tableCell"); |
michael@0 | 9229 | AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame, "bcCell", "bcTableCell"); |
michael@0 | 9230 | AddFrameTypeInfo(nsGkAtoms::tableColFrame, "col", "tableCol"); |
michael@0 | 9231 | AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame, "colG", "tableColGroup"); |
michael@0 | 9232 | AddFrameTypeInfo(nsGkAtoms::tableFrame, "tbl", "table"); |
michael@0 | 9233 | AddFrameTypeInfo(nsGkAtoms::tableOuterFrame, "tblO", "tableOuter"); |
michael@0 | 9234 | AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame, "rowG", "tableRowGroup"); |
michael@0 | 9235 | AddFrameTypeInfo(nsGkAtoms::tableRowFrame, "row", "tableRow"); |
michael@0 | 9236 | AddFrameTypeInfo(nsGkAtoms::textInputFrame, "textCtl", "textInput"); |
michael@0 | 9237 | AddFrameTypeInfo(nsGkAtoms::textFrame, "text", "text"); |
michael@0 | 9238 | AddFrameTypeInfo(nsGkAtoms::viewportFrame, "VP", "viewport"); |
michael@0 | 9239 | #ifdef MOZ_XUL |
michael@0 | 9240 | AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel"); |
michael@0 | 9241 | AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box"); |
michael@0 | 9242 | AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider"); |
michael@0 | 9243 | AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet"); |
michael@0 | 9244 | #endif |
michael@0 | 9245 | AddFrameTypeInfo(nullptr, "unknown", "unknown"); |
michael@0 | 9246 | } |
michael@0 | 9247 | |
michael@0 | 9248 | |
michael@0 | 9249 | void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, |
michael@0 | 9250 | int32_t aIndent) |
michael@0 | 9251 | { |
michael@0 | 9252 | DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType()); |
michael@0 | 9253 | if (frameTypeInfo) { |
michael@0 | 9254 | for (int32_t i = 0; i < aIndent; i++) { |
michael@0 | 9255 | printf(" "); |
michael@0 | 9256 | } |
michael@0 | 9257 | if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) { |
michael@0 | 9258 | if (aFrame) { |
michael@0 | 9259 | nsAutoString name; |
michael@0 | 9260 | aFrame->GetFrameName(name); |
michael@0 | 9261 | printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame); |
michael@0 | 9262 | } |
michael@0 | 9263 | else { |
michael@0 | 9264 | printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); |
michael@0 | 9265 | } |
michael@0 | 9266 | } |
michael@0 | 9267 | else { |
michael@0 | 9268 | printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); |
michael@0 | 9269 | } |
michael@0 | 9270 | } |
michael@0 | 9271 | } |
michael@0 | 9272 | |
michael@0 | 9273 | bool DR_State::RuleMatches(DR_Rule& aRule, |
michael@0 | 9274 | DR_FrameTreeNode& aNode) |
michael@0 | 9275 | { |
michael@0 | 9276 | NS_ASSERTION(aRule.mTarget, "program error"); |
michael@0 | 9277 | |
michael@0 | 9278 | DR_RulePart* rulePart; |
michael@0 | 9279 | DR_FrameTreeNode* parentNode; |
michael@0 | 9280 | for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent; |
michael@0 | 9281 | rulePart && parentNode; |
michael@0 | 9282 | rulePart = rulePart->mNext, parentNode = parentNode->mParent) { |
michael@0 | 9283 | if (rulePart->mFrameType) { |
michael@0 | 9284 | if (parentNode->mFrame) { |
michael@0 | 9285 | if (rulePart->mFrameType != parentNode->mFrame->GetType()) { |
michael@0 | 9286 | return false; |
michael@0 | 9287 | } |
michael@0 | 9288 | } |
michael@0 | 9289 | else NS_ASSERTION(false, "program error"); |
michael@0 | 9290 | } |
michael@0 | 9291 | // else wild card match |
michael@0 | 9292 | } |
michael@0 | 9293 | return true; |
michael@0 | 9294 | } |
michael@0 | 9295 | |
michael@0 | 9296 | void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) |
michael@0 | 9297 | { |
michael@0 | 9298 | if (!aNode.mFrame) { |
michael@0 | 9299 | NS_ASSERTION(false, "invalid DR_FrameTreeNode \n"); |
michael@0 | 9300 | return; |
michael@0 | 9301 | } |
michael@0 | 9302 | |
michael@0 | 9303 | bool matchingRule = false; |
michael@0 | 9304 | |
michael@0 | 9305 | DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->GetType()); |
michael@0 | 9306 | NS_ASSERTION(info, "program error"); |
michael@0 | 9307 | int32_t numRules = info->mRules.Length(); |
michael@0 | 9308 | for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { |
michael@0 | 9309 | DR_Rule* rule = info->mRules.ElementAt(ruleX); |
michael@0 | 9310 | if (rule && RuleMatches(*rule, aNode)) { |
michael@0 | 9311 | aNode.mDisplay = rule->mDisplay; |
michael@0 | 9312 | matchingRule = true; |
michael@0 | 9313 | break; |
michael@0 | 9314 | } |
michael@0 | 9315 | } |
michael@0 | 9316 | if (!matchingRule) { |
michael@0 | 9317 | int32_t numWildRules = mWildRules.Length(); |
michael@0 | 9318 | for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) { |
michael@0 | 9319 | DR_Rule* rule = mWildRules.ElementAt(ruleX); |
michael@0 | 9320 | if (rule && RuleMatches(*rule, aNode)) { |
michael@0 | 9321 | aNode.mDisplay = rule->mDisplay; |
michael@0 | 9322 | break; |
michael@0 | 9323 | } |
michael@0 | 9324 | } |
michael@0 | 9325 | } |
michael@0 | 9326 | } |
michael@0 | 9327 | |
michael@0 | 9328 | DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame, |
michael@0 | 9329 | const nsHTMLReflowState* aReflowState) |
michael@0 | 9330 | { |
michael@0 | 9331 | // find the frame of the parent reflow state (usually just the parent of aFrame) |
michael@0 | 9332 | nsIFrame* parentFrame; |
michael@0 | 9333 | if (aReflowState) { |
michael@0 | 9334 | const nsHTMLReflowState* parentRS = aReflowState->parentReflowState; |
michael@0 | 9335 | parentFrame = (parentRS) ? parentRS->frame : nullptr; |
michael@0 | 9336 | } else { |
michael@0 | 9337 | parentFrame = aFrame->GetParent(); |
michael@0 | 9338 | } |
michael@0 | 9339 | |
michael@0 | 9340 | // find the parent tree node leaf |
michael@0 | 9341 | DR_FrameTreeNode* parentNode = nullptr; |
michael@0 | 9342 | |
michael@0 | 9343 | DR_FrameTreeNode* lastLeaf = nullptr; |
michael@0 | 9344 | if(mFrameTreeLeaves.Length()) |
michael@0 | 9345 | lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1); |
michael@0 | 9346 | if (lastLeaf) { |
michael@0 | 9347 | for (parentNode = lastLeaf; parentNode && (parentNode->mFrame != parentFrame); parentNode = parentNode->mParent) { |
michael@0 | 9348 | } |
michael@0 | 9349 | } |
michael@0 | 9350 | DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode); |
michael@0 | 9351 | FindMatchingRule(*newNode); |
michael@0 | 9352 | |
michael@0 | 9353 | newNode->mIndent = mIndent; |
michael@0 | 9354 | if (newNode->mDisplay || mIndentUndisplayedFrames) { |
michael@0 | 9355 | ++mIndent; |
michael@0 | 9356 | } |
michael@0 | 9357 | |
michael@0 | 9358 | if (lastLeaf && (lastLeaf == parentNode)) { |
michael@0 | 9359 | mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1); |
michael@0 | 9360 | } |
michael@0 | 9361 | mFrameTreeLeaves.AppendElement(newNode); |
michael@0 | 9362 | mCount++; |
michael@0 | 9363 | |
michael@0 | 9364 | return newNode; |
michael@0 | 9365 | } |
michael@0 | 9366 | |
michael@0 | 9367 | void DR_State::PrettyUC(nscoord aSize, |
michael@0 | 9368 | char* aBuf) |
michael@0 | 9369 | { |
michael@0 | 9370 | if (NS_UNCONSTRAINEDSIZE == aSize) { |
michael@0 | 9371 | strcpy(aBuf, "UC"); |
michael@0 | 9372 | } |
michael@0 | 9373 | else { |
michael@0 | 9374 | if ((nscoord)0xdeadbeefU == aSize) |
michael@0 | 9375 | { |
michael@0 | 9376 | strcpy(aBuf, "deadbeef"); |
michael@0 | 9377 | } |
michael@0 | 9378 | else { |
michael@0 | 9379 | sprintf(aBuf, "%d", aSize); |
michael@0 | 9380 | } |
michael@0 | 9381 | } |
michael@0 | 9382 | } |
michael@0 | 9383 | |
michael@0 | 9384 | void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin) |
michael@0 | 9385 | { |
michael@0 | 9386 | if (aMargin) { |
michael@0 | 9387 | char t[16], r[16], b[16], l[16]; |
michael@0 | 9388 | PrettyUC(aMargin->top, t); |
michael@0 | 9389 | PrettyUC(aMargin->right, r); |
michael@0 | 9390 | PrettyUC(aMargin->bottom, b); |
michael@0 | 9391 | PrettyUC(aMargin->left, l); |
michael@0 | 9392 | printf(" %s=%s,%s,%s,%s", tag, t, r, b, l); |
michael@0 | 9393 | } else { |
michael@0 | 9394 | // use %p here for consistency with other null-pointer printouts |
michael@0 | 9395 | printf(" %s=%p", tag, (void*)aMargin); |
michael@0 | 9396 | } |
michael@0 | 9397 | } |
michael@0 | 9398 | |
michael@0 | 9399 | void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) |
michael@0 | 9400 | { |
michael@0 | 9401 | mFrameTreeLeaves.RemoveElement(&aNode); |
michael@0 | 9402 | int32_t numLeaves = mFrameTreeLeaves.Length(); |
michael@0 | 9403 | if ((0 == numLeaves) || (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) { |
michael@0 | 9404 | mFrameTreeLeaves.AppendElement(aNode.mParent); |
michael@0 | 9405 | } |
michael@0 | 9406 | |
michael@0 | 9407 | if (aNode.mDisplay || mIndentUndisplayedFrames) { |
michael@0 | 9408 | --mIndent; |
michael@0 | 9409 | } |
michael@0 | 9410 | // delete the tree node |
michael@0 | 9411 | delete &aNode; |
michael@0 | 9412 | } |
michael@0 | 9413 | |
michael@0 | 9414 | static void |
michael@0 | 9415 | CheckPixelError(nscoord aSize, |
michael@0 | 9416 | int32_t aPixelToTwips) |
michael@0 | 9417 | { |
michael@0 | 9418 | if (NS_UNCONSTRAINEDSIZE != aSize) { |
michael@0 | 9419 | if ((aSize % aPixelToTwips) > 0) { |
michael@0 | 9420 | printf("VALUE %d is not a whole pixel \n", aSize); |
michael@0 | 9421 | } |
michael@0 | 9422 | } |
michael@0 | 9423 | } |
michael@0 | 9424 | |
michael@0 | 9425 | static void DisplayReflowEnterPrint(nsPresContext* aPresContext, |
michael@0 | 9426 | nsIFrame* aFrame, |
michael@0 | 9427 | const nsHTMLReflowState& aReflowState, |
michael@0 | 9428 | DR_FrameTreeNode& aTreeNode, |
michael@0 | 9429 | bool aChanged) |
michael@0 | 9430 | { |
michael@0 | 9431 | if (aTreeNode.mDisplay) { |
michael@0 | 9432 | DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent); |
michael@0 | 9433 | |
michael@0 | 9434 | char width[16]; |
michael@0 | 9435 | char height[16]; |
michael@0 | 9436 | |
michael@0 | 9437 | DR_state->PrettyUC(aReflowState.AvailableWidth(), width); |
michael@0 | 9438 | DR_state->PrettyUC(aReflowState.AvailableHeight(), height); |
michael@0 | 9439 | printf("Reflow a=%s,%s ", width, height); |
michael@0 | 9440 | |
michael@0 | 9441 | DR_state->PrettyUC(aReflowState.ComputedWidth(), width); |
michael@0 | 9442 | DR_state->PrettyUC(aReflowState.ComputedHeight(), height); |
michael@0 | 9443 | printf("c=%s,%s ", width, height); |
michael@0 | 9444 | |
michael@0 | 9445 | if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) |
michael@0 | 9446 | printf("dirty "); |
michael@0 | 9447 | |
michael@0 | 9448 | if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) |
michael@0 | 9449 | printf("dirty-children "); |
michael@0 | 9450 | |
michael@0 | 9451 | if (aReflowState.mFlags.mSpecialHeightReflow) |
michael@0 | 9452 | printf("special-height "); |
michael@0 | 9453 | |
michael@0 | 9454 | if (aReflowState.mFlags.mHResize) |
michael@0 | 9455 | printf("h-resize "); |
michael@0 | 9456 | |
michael@0 | 9457 | if (aReflowState.mFlags.mVResize) |
michael@0 | 9458 | printf("v-resize "); |
michael@0 | 9459 | |
michael@0 | 9460 | nsIFrame* inFlow = aFrame->GetPrevInFlow(); |
michael@0 | 9461 | if (inFlow) { |
michael@0 | 9462 | printf("pif=%p ", (void*)inFlow); |
michael@0 | 9463 | } |
michael@0 | 9464 | inFlow = aFrame->GetNextInFlow(); |
michael@0 | 9465 | if (inFlow) { |
michael@0 | 9466 | printf("nif=%p ", (void*)inFlow); |
michael@0 | 9467 | } |
michael@0 | 9468 | if (aChanged) |
michael@0 | 9469 | printf("CHANGED \n"); |
michael@0 | 9470 | else |
michael@0 | 9471 | printf("cnt=%d \n", DR_state->mCount); |
michael@0 | 9472 | if (DR_state->mDisplayPixelErrors) { |
michael@0 | 9473 | int32_t p2t = aPresContext->AppUnitsPerDevPixel(); |
michael@0 | 9474 | CheckPixelError(aReflowState.AvailableWidth(), p2t); |
michael@0 | 9475 | CheckPixelError(aReflowState.AvailableHeight(), p2t); |
michael@0 | 9476 | CheckPixelError(aReflowState.ComputedWidth(), p2t); |
michael@0 | 9477 | CheckPixelError(aReflowState.ComputedHeight(), p2t); |
michael@0 | 9478 | } |
michael@0 | 9479 | } |
michael@0 | 9480 | } |
michael@0 | 9481 | |
michael@0 | 9482 | void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext, |
michael@0 | 9483 | nsIFrame* aFrame, |
michael@0 | 9484 | const nsHTMLReflowState& aReflowState) |
michael@0 | 9485 | { |
michael@0 | 9486 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9487 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9488 | |
michael@0 | 9489 | NS_ASSERTION(aFrame, "invalid call"); |
michael@0 | 9490 | |
michael@0 | 9491 | DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowState); |
michael@0 | 9492 | if (treeNode) { |
michael@0 | 9493 | DisplayReflowEnterPrint(aPresContext, aFrame, aReflowState, *treeNode, false); |
michael@0 | 9494 | } |
michael@0 | 9495 | return treeNode; |
michael@0 | 9496 | } |
michael@0 | 9497 | |
michael@0 | 9498 | void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame) |
michael@0 | 9499 | { |
michael@0 | 9500 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9501 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9502 | |
michael@0 | 9503 | NS_ASSERTION(aFrame, "invalid call"); |
michael@0 | 9504 | |
michael@0 | 9505 | DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
michael@0 | 9506 | if (treeNode && treeNode->mDisplay) { |
michael@0 | 9507 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9508 | printf("Layout\n"); |
michael@0 | 9509 | } |
michael@0 | 9510 | return treeNode; |
michael@0 | 9511 | } |
michael@0 | 9512 | |
michael@0 | 9513 | void* nsFrame::DisplayIntrinsicWidthEnter(nsIFrame* aFrame, |
michael@0 | 9514 | const char* aType) |
michael@0 | 9515 | { |
michael@0 | 9516 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9517 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9518 | |
michael@0 | 9519 | NS_ASSERTION(aFrame, "invalid call"); |
michael@0 | 9520 | |
michael@0 | 9521 | DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
michael@0 | 9522 | if (treeNode && treeNode->mDisplay) { |
michael@0 | 9523 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9524 | printf("Get%sWidth\n", aType); |
michael@0 | 9525 | } |
michael@0 | 9526 | return treeNode; |
michael@0 | 9527 | } |
michael@0 | 9528 | |
michael@0 | 9529 | void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, |
michael@0 | 9530 | const char* aType) |
michael@0 | 9531 | { |
michael@0 | 9532 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9533 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9534 | |
michael@0 | 9535 | NS_ASSERTION(aFrame, "invalid call"); |
michael@0 | 9536 | |
michael@0 | 9537 | DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
michael@0 | 9538 | if (treeNode && treeNode->mDisplay) { |
michael@0 | 9539 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9540 | printf("Get%sSize\n", aType); |
michael@0 | 9541 | } |
michael@0 | 9542 | return treeNode; |
michael@0 | 9543 | } |
michael@0 | 9544 | |
michael@0 | 9545 | void nsFrame::DisplayReflowExit(nsPresContext* aPresContext, |
michael@0 | 9546 | nsIFrame* aFrame, |
michael@0 | 9547 | nsHTMLReflowMetrics& aMetrics, |
michael@0 | 9548 | nsReflowStatus aStatus, |
michael@0 | 9549 | void* aFrameTreeNode) |
michael@0 | 9550 | { |
michael@0 | 9551 | if (!DR_state->mActive) return; |
michael@0 | 9552 | |
michael@0 | 9553 | NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call"); |
michael@0 | 9554 | if (!aFrameTreeNode) return; |
michael@0 | 9555 | |
michael@0 | 9556 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
michael@0 | 9557 | if (treeNode->mDisplay) { |
michael@0 | 9558 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9559 | |
michael@0 | 9560 | char width[16]; |
michael@0 | 9561 | char height[16]; |
michael@0 | 9562 | char x[16]; |
michael@0 | 9563 | char y[16]; |
michael@0 | 9564 | DR_state->PrettyUC(aMetrics.Width(), width); |
michael@0 | 9565 | DR_state->PrettyUC(aMetrics.Height(), height); |
michael@0 | 9566 | printf("Reflow d=%s,%s", width, height); |
michael@0 | 9567 | |
michael@0 | 9568 | if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { |
michael@0 | 9569 | printf(" status=0x%x", aStatus); |
michael@0 | 9570 | } |
michael@0 | 9571 | if (aFrame->HasOverflowAreas()) { |
michael@0 | 9572 | DR_state->PrettyUC(aMetrics.VisualOverflow().x, x); |
michael@0 | 9573 | DR_state->PrettyUC(aMetrics.VisualOverflow().y, y); |
michael@0 | 9574 | DR_state->PrettyUC(aMetrics.VisualOverflow().width, width); |
michael@0 | 9575 | DR_state->PrettyUC(aMetrics.VisualOverflow().height, height); |
michael@0 | 9576 | printf(" vis-o=(%s,%s) %s x %s", x, y, width, height); |
michael@0 | 9577 | |
michael@0 | 9578 | nsRect storedOverflow = aFrame->GetVisualOverflowRect(); |
michael@0 | 9579 | DR_state->PrettyUC(storedOverflow.x, x); |
michael@0 | 9580 | DR_state->PrettyUC(storedOverflow.y, y); |
michael@0 | 9581 | DR_state->PrettyUC(storedOverflow.width, width); |
michael@0 | 9582 | DR_state->PrettyUC(storedOverflow.height, height); |
michael@0 | 9583 | printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height); |
michael@0 | 9584 | |
michael@0 | 9585 | DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x); |
michael@0 | 9586 | DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y); |
michael@0 | 9587 | DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width); |
michael@0 | 9588 | DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height); |
michael@0 | 9589 | printf(" scr-o=(%s,%s) %s x %s", x, y, width, height); |
michael@0 | 9590 | |
michael@0 | 9591 | storedOverflow = aFrame->GetScrollableOverflowRect(); |
michael@0 | 9592 | DR_state->PrettyUC(storedOverflow.x, x); |
michael@0 | 9593 | DR_state->PrettyUC(storedOverflow.y, y); |
michael@0 | 9594 | DR_state->PrettyUC(storedOverflow.width, width); |
michael@0 | 9595 | DR_state->PrettyUC(storedOverflow.height, height); |
michael@0 | 9596 | printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height); |
michael@0 | 9597 | } |
michael@0 | 9598 | printf("\n"); |
michael@0 | 9599 | if (DR_state->mDisplayPixelErrors) { |
michael@0 | 9600 | int32_t p2t = aPresContext->AppUnitsPerDevPixel(); |
michael@0 | 9601 | CheckPixelError(aMetrics.Width(), p2t); |
michael@0 | 9602 | CheckPixelError(aMetrics.Height(), p2t); |
michael@0 | 9603 | } |
michael@0 | 9604 | } |
michael@0 | 9605 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9606 | } |
michael@0 | 9607 | |
michael@0 | 9608 | void nsFrame::DisplayLayoutExit(nsIFrame* aFrame, |
michael@0 | 9609 | void* aFrameTreeNode) |
michael@0 | 9610 | { |
michael@0 | 9611 | if (!DR_state->mActive) return; |
michael@0 | 9612 | |
michael@0 | 9613 | NS_ASSERTION(aFrame, "non-null frame required"); |
michael@0 | 9614 | if (!aFrameTreeNode) return; |
michael@0 | 9615 | |
michael@0 | 9616 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
michael@0 | 9617 | if (treeNode->mDisplay) { |
michael@0 | 9618 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9619 | nsRect rect = aFrame->GetRect(); |
michael@0 | 9620 | printf("Layout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height); |
michael@0 | 9621 | } |
michael@0 | 9622 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9623 | } |
michael@0 | 9624 | |
michael@0 | 9625 | void nsFrame::DisplayIntrinsicWidthExit(nsIFrame* aFrame, |
michael@0 | 9626 | const char* aType, |
michael@0 | 9627 | nscoord aResult, |
michael@0 | 9628 | void* aFrameTreeNode) |
michael@0 | 9629 | { |
michael@0 | 9630 | if (!DR_state->mActive) return; |
michael@0 | 9631 | |
michael@0 | 9632 | NS_ASSERTION(aFrame, "non-null frame required"); |
michael@0 | 9633 | if (!aFrameTreeNode) return; |
michael@0 | 9634 | |
michael@0 | 9635 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
michael@0 | 9636 | if (treeNode->mDisplay) { |
michael@0 | 9637 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9638 | char width[16]; |
michael@0 | 9639 | DR_state->PrettyUC(aResult, width); |
michael@0 | 9640 | printf("Get%sWidth=%s\n", aType, width); |
michael@0 | 9641 | } |
michael@0 | 9642 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9643 | } |
michael@0 | 9644 | |
michael@0 | 9645 | void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, |
michael@0 | 9646 | const char* aType, |
michael@0 | 9647 | nsSize aResult, |
michael@0 | 9648 | void* aFrameTreeNode) |
michael@0 | 9649 | { |
michael@0 | 9650 | if (!DR_state->mActive) return; |
michael@0 | 9651 | |
michael@0 | 9652 | NS_ASSERTION(aFrame, "non-null frame required"); |
michael@0 | 9653 | if (!aFrameTreeNode) return; |
michael@0 | 9654 | |
michael@0 | 9655 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; |
michael@0 | 9656 | if (treeNode->mDisplay) { |
michael@0 | 9657 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9658 | |
michael@0 | 9659 | char width[16]; |
michael@0 | 9660 | char height[16]; |
michael@0 | 9661 | DR_state->PrettyUC(aResult.width, width); |
michael@0 | 9662 | DR_state->PrettyUC(aResult.height, height); |
michael@0 | 9663 | printf("Get%sSize=%s,%s\n", aType, width, height); |
michael@0 | 9664 | } |
michael@0 | 9665 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9666 | } |
michael@0 | 9667 | |
michael@0 | 9668 | /* static */ void |
michael@0 | 9669 | nsFrame::DisplayReflowStartup() |
michael@0 | 9670 | { |
michael@0 | 9671 | DR_state = new DR_State(); |
michael@0 | 9672 | } |
michael@0 | 9673 | |
michael@0 | 9674 | /* static */ void |
michael@0 | 9675 | nsFrame::DisplayReflowShutdown() |
michael@0 | 9676 | { |
michael@0 | 9677 | delete DR_state; |
michael@0 | 9678 | DR_state = nullptr; |
michael@0 | 9679 | } |
michael@0 | 9680 | |
michael@0 | 9681 | void DR_cookie::Change() const |
michael@0 | 9682 | { |
michael@0 | 9683 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue; |
michael@0 | 9684 | if (treeNode && treeNode->mDisplay) { |
michael@0 | 9685 | DisplayReflowEnterPrint(mPresContext, mFrame, mReflowState, *treeNode, true); |
michael@0 | 9686 | } |
michael@0 | 9687 | } |
michael@0 | 9688 | |
michael@0 | 9689 | /* static */ void* |
michael@0 | 9690 | nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame, |
michael@0 | 9691 | nsHTMLReflowState* aState, |
michael@0 | 9692 | nscoord aContainingBlockWidth, |
michael@0 | 9693 | nscoord aContainingBlockHeight, |
michael@0 | 9694 | const nsMargin* aBorder, |
michael@0 | 9695 | const nsMargin* aPadding) |
michael@0 | 9696 | { |
michael@0 | 9697 | NS_PRECONDITION(aFrame, "non-null frame required"); |
michael@0 | 9698 | NS_PRECONDITION(aState, "non-null state required"); |
michael@0 | 9699 | |
michael@0 | 9700 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9701 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9702 | |
michael@0 | 9703 | DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState); |
michael@0 | 9704 | if (treeNode && treeNode->mDisplay) { |
michael@0 | 9705 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9706 | |
michael@0 | 9707 | printf("InitConstraints parent=%p", |
michael@0 | 9708 | (void*)aState->parentReflowState); |
michael@0 | 9709 | |
michael@0 | 9710 | char width[16]; |
michael@0 | 9711 | char height[16]; |
michael@0 | 9712 | |
michael@0 | 9713 | DR_state->PrettyUC(aContainingBlockWidth, width); |
michael@0 | 9714 | DR_state->PrettyUC(aContainingBlockHeight, height); |
michael@0 | 9715 | printf(" cb=%s,%s", width, height); |
michael@0 | 9716 | |
michael@0 | 9717 | DR_state->PrettyUC(aState->AvailableWidth(), width); |
michael@0 | 9718 | DR_state->PrettyUC(aState->AvailableHeight(), height); |
michael@0 | 9719 | printf(" as=%s,%s", width, height); |
michael@0 | 9720 | |
michael@0 | 9721 | DR_state->PrintMargin("b", aBorder); |
michael@0 | 9722 | DR_state->PrintMargin("p", aPadding); |
michael@0 | 9723 | putchar('\n'); |
michael@0 | 9724 | } |
michael@0 | 9725 | return treeNode; |
michael@0 | 9726 | } |
michael@0 | 9727 | |
michael@0 | 9728 | /* static */ void |
michael@0 | 9729 | nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame, |
michael@0 | 9730 | nsHTMLReflowState* aState, |
michael@0 | 9731 | void* aValue) |
michael@0 | 9732 | { |
michael@0 | 9733 | NS_PRECONDITION(aFrame, "non-null frame required"); |
michael@0 | 9734 | NS_PRECONDITION(aState, "non-null state required"); |
michael@0 | 9735 | |
michael@0 | 9736 | if (!DR_state->mActive) return; |
michael@0 | 9737 | if (!aValue) return; |
michael@0 | 9738 | |
michael@0 | 9739 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; |
michael@0 | 9740 | if (treeNode->mDisplay) { |
michael@0 | 9741 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9742 | char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16]; |
michael@0 | 9743 | DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw); |
michael@0 | 9744 | DR_state->PrettyUC(aState->ComputedWidth(), cw); |
michael@0 | 9745 | DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw); |
michael@0 | 9746 | DR_state->PrettyUC(aState->ComputedMinHeight(), cmih); |
michael@0 | 9747 | DR_state->PrettyUC(aState->ComputedHeight(), ch); |
michael@0 | 9748 | DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh); |
michael@0 | 9749 | printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", |
michael@0 | 9750 | cmiw, cw, cmxw, cmih, ch, cmxh); |
michael@0 | 9751 | DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets()); |
michael@0 | 9752 | putchar('\n'); |
michael@0 | 9753 | } |
michael@0 | 9754 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9755 | } |
michael@0 | 9756 | |
michael@0 | 9757 | |
michael@0 | 9758 | /* static */ void* |
michael@0 | 9759 | nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame, |
michael@0 | 9760 | nsCSSOffsetState* aState, |
michael@0 | 9761 | nscoord aHorizontalPercentBasis, |
michael@0 | 9762 | nscoord aVerticalPercentBasis, |
michael@0 | 9763 | const nsMargin* aBorder, |
michael@0 | 9764 | const nsMargin* aPadding) |
michael@0 | 9765 | { |
michael@0 | 9766 | NS_PRECONDITION(aFrame, "non-null frame required"); |
michael@0 | 9767 | NS_PRECONDITION(aState, "non-null state required"); |
michael@0 | 9768 | |
michael@0 | 9769 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9770 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9771 | |
michael@0 | 9772 | // aState is not necessarily a nsHTMLReflowState |
michael@0 | 9773 | DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); |
michael@0 | 9774 | if (treeNode && treeNode->mDisplay) { |
michael@0 | 9775 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9776 | |
michael@0 | 9777 | char horizPctBasisStr[16]; |
michael@0 | 9778 | char vertPctBasisStr[16]; |
michael@0 | 9779 | DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr); |
michael@0 | 9780 | DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr); |
michael@0 | 9781 | printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr); |
michael@0 | 9782 | |
michael@0 | 9783 | DR_state->PrintMargin("b", aBorder); |
michael@0 | 9784 | DR_state->PrintMargin("p", aPadding); |
michael@0 | 9785 | putchar('\n'); |
michael@0 | 9786 | } |
michael@0 | 9787 | return treeNode; |
michael@0 | 9788 | } |
michael@0 | 9789 | |
michael@0 | 9790 | /* static */ void |
michael@0 | 9791 | nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame* aFrame, |
michael@0 | 9792 | nsCSSOffsetState* aState, |
michael@0 | 9793 | void* aValue) |
michael@0 | 9794 | { |
michael@0 | 9795 | NS_PRECONDITION(aFrame, "non-null frame required"); |
michael@0 | 9796 | NS_PRECONDITION(aState, "non-null state required"); |
michael@0 | 9797 | |
michael@0 | 9798 | if (!DR_state->mActive) return; |
michael@0 | 9799 | if (!aValue) return; |
michael@0 | 9800 | |
michael@0 | 9801 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; |
michael@0 | 9802 | if (treeNode->mDisplay) { |
michael@0 | 9803 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9804 | printf("InitOffsets="); |
michael@0 | 9805 | DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin()); |
michael@0 | 9806 | DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding()); |
michael@0 | 9807 | DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding()); |
michael@0 | 9808 | putchar('\n'); |
michael@0 | 9809 | } |
michael@0 | 9810 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9811 | } |
michael@0 | 9812 | |
michael@0 | 9813 | /* static */ void* |
michael@0 | 9814 | nsHTMLReflowState::DisplayInitFrameTypeEnter(nsIFrame* aFrame, |
michael@0 | 9815 | nsHTMLReflowState* aState) |
michael@0 | 9816 | { |
michael@0 | 9817 | NS_PRECONDITION(aFrame, "non-null frame required"); |
michael@0 | 9818 | NS_PRECONDITION(aState, "non-null state required"); |
michael@0 | 9819 | |
michael@0 | 9820 | if (!DR_state->mInited) DR_state->Init(); |
michael@0 | 9821 | if (!DR_state->mActive) return nullptr; |
michael@0 | 9822 | |
michael@0 | 9823 | // we don't print anything here |
michael@0 | 9824 | return DR_state->CreateTreeNode(aFrame, aState); |
michael@0 | 9825 | } |
michael@0 | 9826 | |
michael@0 | 9827 | /* static */ void |
michael@0 | 9828 | nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame* aFrame, |
michael@0 | 9829 | nsHTMLReflowState* aState, |
michael@0 | 9830 | void* aValue) |
michael@0 | 9831 | { |
michael@0 | 9832 | NS_PRECONDITION(aFrame, "non-null frame required"); |
michael@0 | 9833 | NS_PRECONDITION(aState, "non-null state required"); |
michael@0 | 9834 | |
michael@0 | 9835 | if (!DR_state->mActive) return; |
michael@0 | 9836 | if (!aValue) return; |
michael@0 | 9837 | |
michael@0 | 9838 | DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; |
michael@0 | 9839 | if (treeNode->mDisplay) { |
michael@0 | 9840 | DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); |
michael@0 | 9841 | printf("InitFrameType"); |
michael@0 | 9842 | |
michael@0 | 9843 | const nsStyleDisplay *disp = aState->mStyleDisplay; |
michael@0 | 9844 | |
michael@0 | 9845 | if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) |
michael@0 | 9846 | printf(" out-of-flow"); |
michael@0 | 9847 | if (aFrame->GetPrevInFlow()) |
michael@0 | 9848 | printf(" prev-in-flow"); |
michael@0 | 9849 | if (aFrame->IsAbsolutelyPositioned()) |
michael@0 | 9850 | printf(" abspos"); |
michael@0 | 9851 | if (aFrame->IsFloating()) |
michael@0 | 9852 | printf(" float"); |
michael@0 | 9853 | |
michael@0 | 9854 | // This array must exactly match the NS_STYLE_DISPLAY constants. |
michael@0 | 9855 | const char *const displayTypes[] = { |
michael@0 | 9856 | "none", "block", "inline", "inline-block", "list-item", "marker", |
michael@0 | 9857 | "run-in", "compact", "table", "inline-table", "table-row-group", |
michael@0 | 9858 | "table-column", "table-column-group", "table-header-group", |
michael@0 | 9859 | "table-footer-group", "table-row", "table-cell", "table-caption", |
michael@0 | 9860 | "box", "inline-box", |
michael@0 | 9861 | #ifdef MOZ_XUL |
michael@0 | 9862 | "grid", "inline-grid", "grid-group", "grid-line", "stack", |
michael@0 | 9863 | "inline-stack", "deck", "popup", "groupbox", |
michael@0 | 9864 | #endif |
michael@0 | 9865 | }; |
michael@0 | 9866 | if (disp->mDisplay >= ArrayLength(displayTypes)) |
michael@0 | 9867 | printf(" display=%u", disp->mDisplay); |
michael@0 | 9868 | else |
michael@0 | 9869 | printf(" display=%s", displayTypes[disp->mDisplay]); |
michael@0 | 9870 | |
michael@0 | 9871 | // This array must exactly match the NS_CSS_FRAME_TYPE constants. |
michael@0 | 9872 | const char *const cssFrameTypes[] = { |
michael@0 | 9873 | "unknown", "inline", "block", "floating", "absolute", "internal-table" |
michael@0 | 9874 | }; |
michael@0 | 9875 | nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType); |
michael@0 | 9876 | bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType); |
michael@0 | 9877 | bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType); |
michael@0 | 9878 | |
michael@0 | 9879 | if (bareType >= ArrayLength(cssFrameTypes)) { |
michael@0 | 9880 | printf(" result=type %u", bareType); |
michael@0 | 9881 | } else { |
michael@0 | 9882 | printf(" result=%s", cssFrameTypes[bareType]); |
michael@0 | 9883 | } |
michael@0 | 9884 | printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : ""); |
michael@0 | 9885 | } |
michael@0 | 9886 | DR_state->DeleteTreeNode(*treeNode); |
michael@0 | 9887 | } |
michael@0 | 9888 | |
michael@0 | 9889 | #endif |
michael@0 | 9890 | // End Display Reflow |
michael@0 | 9891 | |
michael@0 | 9892 | #endif |