layout/generic/nsFrame.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 // 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("&lt;"));
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("&gt;"));
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("&quot;"));
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 &current, &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

mercurial