layout/base/nsFrameTraversal.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5 #include "nsCOMPtr.h"
michael@0 6 #include "nsGkAtoms.h"
michael@0 7
michael@0 8 #include "nsFrameTraversal.h"
michael@0 9 #include "nsFrameList.h"
michael@0 10 #include "nsPlaceholderFrame.h"
michael@0 11
michael@0 12
michael@0 13 class nsFrameIterator : public nsIFrameEnumerator
michael@0 14 {
michael@0 15 public:
michael@0 16 typedef nsIFrame::ChildListID ChildListID;
michael@0 17
michael@0 18 NS_DECL_ISUPPORTS
michael@0 19
michael@0 20 virtual ~nsFrameIterator() {}
michael@0 21
michael@0 22 virtual void First() MOZ_OVERRIDE;
michael@0 23 virtual void Next() MOZ_OVERRIDE;
michael@0 24 virtual nsIFrame* CurrentItem() MOZ_OVERRIDE;
michael@0 25 virtual bool IsDone() MOZ_OVERRIDE;
michael@0 26
michael@0 27 virtual void Last() MOZ_OVERRIDE;
michael@0 28 virtual void Prev() MOZ_OVERRIDE;
michael@0 29
michael@0 30 nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
michael@0 31 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs);
michael@0 32
michael@0 33 protected:
michael@0 34 void setCurrent(nsIFrame *aFrame){mCurrent = aFrame;}
michael@0 35 nsIFrame *getCurrent(){return mCurrent;}
michael@0 36 nsIFrame *getStart(){return mStart;}
michael@0 37 nsIFrame *getLast(){return mLast;}
michael@0 38 void setLast(nsIFrame *aFrame){mLast = aFrame;}
michael@0 39 int8_t getOffEdge(){return mOffEdge;}
michael@0 40 void setOffEdge(int8_t aOffEdge){mOffEdge = aOffEdge;}
michael@0 41
michael@0 42 /*
michael@0 43 Our own versions of the standard frame tree navigation
michael@0 44 methods, which, if the iterator is following out-of-flows,
michael@0 45 apply the following rules for placeholder frames:
michael@0 46
michael@0 47 - If a frame HAS a placeholder frame, getting its parent
michael@0 48 gets the placeholder's parent.
michael@0 49
michael@0 50 - If a frame's first child or next/prev sibling IS a
michael@0 51 placeholder frame, then we instead return the real frame.
michael@0 52
michael@0 53 - If a frame HAS a placeholder frame, getting its next/prev
michael@0 54 sibling gets the placeholder frame's next/prev sibling.
michael@0 55
michael@0 56 These are all applied recursively to support multiple levels of
michael@0 57 placeholders.
michael@0 58 */
michael@0 59
michael@0 60 nsIFrame* GetParentFrame(nsIFrame* aFrame);
michael@0 61 // like GetParentFrame but returns null once a popup frame is reached
michael@0 62 nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame);
michael@0 63
michael@0 64 nsIFrame* GetFirstChild(nsIFrame* aFrame);
michael@0 65 nsIFrame* GetLastChild(nsIFrame* aFrame);
michael@0 66
michael@0 67 nsIFrame* GetNextSibling(nsIFrame* aFrame);
michael@0 68 nsIFrame* GetPrevSibling(nsIFrame* aFrame);
michael@0 69
michael@0 70 /*
michael@0 71 These methods are overridden by the bidi visual iterator to have the
michael@0 72 semantics of "get first child in visual order", "get last child in visual
michael@0 73 order", "get next sibling in visual order" and "get previous sibling in visual
michael@0 74 order".
michael@0 75 */
michael@0 76
michael@0 77 virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame);
michael@0 78 virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame);
michael@0 79
michael@0 80 virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame);
michael@0 81 virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame);
michael@0 82
michael@0 83 nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
michael@0 84 bool IsPopupFrame(nsIFrame* aFrame);
michael@0 85
michael@0 86 nsPresContext* const mPresContext;
michael@0 87 const bool mLockScroll;
michael@0 88 const bool mFollowOOFs;
michael@0 89 const nsIteratorType mType;
michael@0 90
michael@0 91 private:
michael@0 92 nsIFrame* const mStart;
michael@0 93 nsIFrame* mCurrent;
michael@0 94 nsIFrame* mLast; //the last one that was in current;
michael@0 95 int8_t mOffEdge; //0= no -1 to far prev, 1 to far next;
michael@0 96 };
michael@0 97
michael@0 98
michael@0 99
michael@0 100 // Bidi visual iterator
michael@0 101 class nsVisualIterator: public nsFrameIterator
michael@0 102 {
michael@0 103 public:
michael@0 104 nsVisualIterator(nsPresContext* aPresContext, nsIFrame *aStart,
michael@0 105 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs) :
michael@0 106 nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs) {}
michael@0 107
michael@0 108 protected:
michael@0 109 nsIFrame* GetFirstChildInner(nsIFrame* aFrame) MOZ_OVERRIDE;
michael@0 110 nsIFrame* GetLastChildInner(nsIFrame* aFrame) MOZ_OVERRIDE;
michael@0 111
michael@0 112 nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE;
michael@0 113 nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE;
michael@0 114 };
michael@0 115
michael@0 116 /************IMPLEMENTATIONS**************/
michael@0 117
michael@0 118 nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult)
michael@0 119 {
michael@0 120 NS_ENSURE_ARG_POINTER(aResult);
michael@0 121 *aResult = nullptr;
michael@0 122
michael@0 123 nsCOMPtr<nsIFrameTraversal> t(new nsFrameTraversal());
michael@0 124
michael@0 125 *aResult = t;
michael@0 126 NS_ADDREF(*aResult);
michael@0 127
michael@0 128 return NS_OK;
michael@0 129 }
michael@0 130
michael@0 131 nsresult
michael@0 132 NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
michael@0 133 nsPresContext* aPresContext,
michael@0 134 nsIFrame *aStart,
michael@0 135 nsIteratorType aType,
michael@0 136 bool aVisual,
michael@0 137 bool aLockInScrollView,
michael@0 138 bool aFollowOOFs)
michael@0 139 {
michael@0 140 if (!aEnumerator || !aStart)
michael@0 141 return NS_ERROR_NULL_POINTER;
michael@0 142
michael@0 143 if (aFollowOOFs) {
michael@0 144 aStart = nsPlaceholderFrame::GetRealFrameFor(aStart);
michael@0 145 }
michael@0 146
michael@0 147 nsCOMPtr<nsIFrameEnumerator> trav;
michael@0 148 if (aVisual) {
michael@0 149 trav = new nsVisualIterator(aPresContext, aStart, aType,
michael@0 150 aLockInScrollView, aFollowOOFs);
michael@0 151 } else {
michael@0 152 trav = new nsFrameIterator(aPresContext, aStart, aType,
michael@0 153 aLockInScrollView, aFollowOOFs);
michael@0 154 }
michael@0 155 trav.forget(aEnumerator);
michael@0 156 return NS_OK;
michael@0 157 }
michael@0 158
michael@0 159
michael@0 160 nsFrameTraversal::nsFrameTraversal()
michael@0 161 {
michael@0 162 }
michael@0 163
michael@0 164 nsFrameTraversal::~nsFrameTraversal()
michael@0 165 {
michael@0 166 }
michael@0 167
michael@0 168 NS_IMPL_ISUPPORTS(nsFrameTraversal,nsIFrameTraversal)
michael@0 169
michael@0 170 NS_IMETHODIMP
michael@0 171 nsFrameTraversal::NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
michael@0 172 nsPresContext* aPresContext,
michael@0 173 nsIFrame *aStart,
michael@0 174 int32_t aType,
michael@0 175 bool aVisual,
michael@0 176 bool aLockInScrollView,
michael@0 177 bool aFollowOOFs)
michael@0 178 {
michael@0 179 return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart,
michael@0 180 static_cast<nsIteratorType>(aType),
michael@0 181 aVisual, aLockInScrollView, aFollowOOFs);
michael@0 182 }
michael@0 183
michael@0 184 // nsFrameIterator implementation
michael@0 185
michael@0 186 NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
michael@0 187
michael@0 188 nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
michael@0 189 nsIteratorType aType, bool aLockInScrollView,
michael@0 190 bool aFollowOOFs)
michael@0 191 : mPresContext(aPresContext),
michael@0 192 mLockScroll(aLockInScrollView),
michael@0 193 mFollowOOFs(aFollowOOFs),
michael@0 194 mType(aType),
michael@0 195 mStart(aStart),
michael@0 196 mCurrent(aStart),
michael@0 197 mLast(aStart),
michael@0 198 mOffEdge(0)
michael@0 199 {
michael@0 200 MOZ_ASSERT(!aFollowOOFs || aStart->GetType() != nsGkAtoms::placeholderFrame,
michael@0 201 "Caller should have resolved placeholder frame");
michael@0 202 }
michael@0 203
michael@0 204
michael@0 205
michael@0 206 nsIFrame*
michael@0 207 nsFrameIterator::CurrentItem()
michael@0 208 {
michael@0 209 if (mOffEdge)
michael@0 210 return nullptr;
michael@0 211
michael@0 212 return mCurrent;
michael@0 213 }
michael@0 214
michael@0 215
michael@0 216
michael@0 217 bool
michael@0 218 nsFrameIterator::IsDone()
michael@0 219 {
michael@0 220 return mOffEdge != 0;
michael@0 221 }
michael@0 222
michael@0 223 void
michael@0 224 nsFrameIterator::First()
michael@0 225 {
michael@0 226 mCurrent = mStart;
michael@0 227 }
michael@0 228
michael@0 229 static bool
michael@0 230 IsRootFrame(nsIFrame* aFrame)
michael@0 231 {
michael@0 232 nsIAtom* atom = aFrame->GetType();
michael@0 233 return (atom == nsGkAtoms::canvasFrame) ||
michael@0 234 (atom == nsGkAtoms::rootFrame);
michael@0 235 }
michael@0 236
michael@0 237 void
michael@0 238 nsFrameIterator::Last()
michael@0 239 {
michael@0 240 nsIFrame* result;
michael@0 241 nsIFrame* parent = getCurrent();
michael@0 242 // If the current frame is a popup, don't move farther up the tree.
michael@0 243 // Otherwise, get the nearest root frame or popup.
michael@0 244 if (parent->GetType() != nsGkAtoms::menuPopupFrame) {
michael@0 245 while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent)))
michael@0 246 parent = result;
michael@0 247 }
michael@0 248
michael@0 249 while ((result = GetLastChild(parent))) {
michael@0 250 parent = result;
michael@0 251 }
michael@0 252
michael@0 253 setCurrent(parent);
michael@0 254 if (!parent)
michael@0 255 setOffEdge(1);
michael@0 256 }
michael@0 257
michael@0 258 void
michael@0 259 nsFrameIterator::Next()
michael@0 260 {
michael@0 261 // recursive-oid method to get next frame
michael@0 262 nsIFrame *result = nullptr;
michael@0 263 nsIFrame *parent = getCurrent();
michael@0 264 if (!parent)
michael@0 265 parent = getLast();
michael@0 266
michael@0 267 if (mType == eLeaf) {
michael@0 268 // Drill down to first leaf
michael@0 269 while ((result = GetFirstChild(parent))) {
michael@0 270 parent = result;
michael@0 271 }
michael@0 272 } else if (mType == ePreOrder) {
michael@0 273 result = GetFirstChild(parent);
michael@0 274 if (result)
michael@0 275 parent = result;
michael@0 276 }
michael@0 277
michael@0 278 if (parent != getCurrent()) {
michael@0 279 result = parent;
michael@0 280 } else {
michael@0 281 while (parent) {
michael@0 282 result = GetNextSibling(parent);
michael@0 283 if (result) {
michael@0 284 if (mType != ePreOrder) {
michael@0 285 parent = result;
michael@0 286 while ((result = GetFirstChild(parent))) {
michael@0 287 parent = result;
michael@0 288 }
michael@0 289 result = parent;
michael@0 290 }
michael@0 291 break;
michael@0 292 }
michael@0 293 else {
michael@0 294 result = GetParentFrameNotPopup(parent);
michael@0 295 if (!result || IsRootFrame(result) ||
michael@0 296 (mLockScroll && result->GetType() == nsGkAtoms::scrollFrame)) {
michael@0 297 result = nullptr;
michael@0 298 break;
michael@0 299 }
michael@0 300 if (mType == ePostOrder)
michael@0 301 break;
michael@0 302 parent = result;
michael@0 303 }
michael@0 304 }
michael@0 305 }
michael@0 306
michael@0 307 setCurrent(result);
michael@0 308 if (!result) {
michael@0 309 setOffEdge(1);
michael@0 310 setLast(parent);
michael@0 311 }
michael@0 312 }
michael@0 313
michael@0 314 void
michael@0 315 nsFrameIterator::Prev()
michael@0 316 {
michael@0 317 // recursive-oid method to get prev frame
michael@0 318 nsIFrame *result = nullptr;
michael@0 319 nsIFrame *parent = getCurrent();
michael@0 320 if (!parent)
michael@0 321 parent = getLast();
michael@0 322
michael@0 323 if (mType == eLeaf) {
michael@0 324 // Drill down to last leaf
michael@0 325 while ((result = GetLastChild(parent))) {
michael@0 326 parent = result;
michael@0 327 }
michael@0 328 } else if (mType == ePostOrder) {
michael@0 329 result = GetLastChild(parent);
michael@0 330 if (result)
michael@0 331 parent = result;
michael@0 332 }
michael@0 333
michael@0 334 if (parent != getCurrent()) {
michael@0 335 result = parent;
michael@0 336 } else {
michael@0 337 while (parent) {
michael@0 338 result = GetPrevSibling(parent);
michael@0 339 if (result) {
michael@0 340 if (mType != ePostOrder) {
michael@0 341 parent = result;
michael@0 342 while ((result = GetLastChild(parent))) {
michael@0 343 parent = result;
michael@0 344 }
michael@0 345 result = parent;
michael@0 346 }
michael@0 347 break;
michael@0 348 } else {
michael@0 349 result = GetParentFrameNotPopup(parent);
michael@0 350 if (!result || IsRootFrame(result) ||
michael@0 351 (mLockScroll && result->GetType() == nsGkAtoms::scrollFrame)) {
michael@0 352 result = nullptr;
michael@0 353 break;
michael@0 354 }
michael@0 355 if (mType == ePreOrder)
michael@0 356 break;
michael@0 357 parent = result;
michael@0 358 }
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362 setCurrent(result);
michael@0 363 if (!result) {
michael@0 364 setOffEdge(-1);
michael@0 365 setLast(parent);
michael@0 366 }
michael@0 367 }
michael@0 368
michael@0 369 nsIFrame*
michael@0 370 nsFrameIterator::GetParentFrame(nsIFrame* aFrame)
michael@0 371 {
michael@0 372 if (mFollowOOFs)
michael@0 373 aFrame = GetPlaceholderFrame(aFrame);
michael@0 374 if (aFrame)
michael@0 375 return aFrame->GetParent();
michael@0 376
michael@0 377 return nullptr;
michael@0 378 }
michael@0 379
michael@0 380 nsIFrame*
michael@0 381 nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame)
michael@0 382 {
michael@0 383 if (mFollowOOFs)
michael@0 384 aFrame = GetPlaceholderFrame(aFrame);
michael@0 385 if (aFrame) {
michael@0 386 nsIFrame* parent = aFrame->GetParent();
michael@0 387 if (!IsPopupFrame(parent))
michael@0 388 return parent;
michael@0 389 }
michael@0 390
michael@0 391 return nullptr;
michael@0 392 }
michael@0 393
michael@0 394 nsIFrame*
michael@0 395 nsFrameIterator::GetFirstChild(nsIFrame* aFrame)
michael@0 396 {
michael@0 397 nsIFrame* result = GetFirstChildInner(aFrame);
michael@0 398 if (mLockScroll && result && result->GetType() == nsGkAtoms::scrollFrame)
michael@0 399 return nullptr;
michael@0 400 if (result && mFollowOOFs) {
michael@0 401 result = nsPlaceholderFrame::GetRealFrameFor(result);
michael@0 402
michael@0 403 if (IsPopupFrame(result))
michael@0 404 result = GetNextSibling(result);
michael@0 405 }
michael@0 406 return result;
michael@0 407 }
michael@0 408
michael@0 409 nsIFrame*
michael@0 410 nsFrameIterator::GetLastChild(nsIFrame* aFrame)
michael@0 411 {
michael@0 412 nsIFrame* result = GetLastChildInner(aFrame);
michael@0 413 if (mLockScroll && result && result->GetType() == nsGkAtoms::scrollFrame)
michael@0 414 return nullptr;
michael@0 415 if (result && mFollowOOFs) {
michael@0 416 result = nsPlaceholderFrame::GetRealFrameFor(result);
michael@0 417
michael@0 418 if (IsPopupFrame(result))
michael@0 419 result = GetPrevSibling(result);
michael@0 420 }
michael@0 421 return result;
michael@0 422 }
michael@0 423
michael@0 424 nsIFrame*
michael@0 425 nsFrameIterator::GetNextSibling(nsIFrame* aFrame)
michael@0 426 {
michael@0 427 nsIFrame* result = nullptr;
michael@0 428 if (mFollowOOFs)
michael@0 429 aFrame = GetPlaceholderFrame(aFrame);
michael@0 430 if (aFrame) {
michael@0 431 result = GetNextSiblingInner(aFrame);
michael@0 432 if (result && mFollowOOFs)
michael@0 433 result = nsPlaceholderFrame::GetRealFrameFor(result);
michael@0 434 }
michael@0 435
michael@0 436 if (mFollowOOFs && IsPopupFrame(result))
michael@0 437 result = GetNextSibling(result);
michael@0 438
michael@0 439 return result;
michael@0 440 }
michael@0 441
michael@0 442 nsIFrame*
michael@0 443 nsFrameIterator::GetPrevSibling(nsIFrame* aFrame)
michael@0 444 {
michael@0 445 nsIFrame* result = nullptr;
michael@0 446 if (mFollowOOFs)
michael@0 447 aFrame = GetPlaceholderFrame(aFrame);
michael@0 448 if (aFrame) {
michael@0 449 result = GetPrevSiblingInner(aFrame);
michael@0 450 if (result && mFollowOOFs)
michael@0 451 result = nsPlaceholderFrame::GetRealFrameFor(result);
michael@0 452 }
michael@0 453
michael@0 454 if (mFollowOOFs && IsPopupFrame(result))
michael@0 455 result = GetPrevSibling(result);
michael@0 456
michael@0 457 return result;
michael@0 458 }
michael@0 459
michael@0 460 nsIFrame*
michael@0 461 nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) {
michael@0 462 return aFrame->GetFirstPrincipalChild();
michael@0 463 }
michael@0 464
michael@0 465 nsIFrame*
michael@0 466 nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) {
michael@0 467 return aFrame->PrincipalChildList().LastChild();
michael@0 468 }
michael@0 469
michael@0 470 nsIFrame*
michael@0 471 nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) {
michael@0 472 return aFrame->GetNextSibling();
michael@0 473 }
michael@0 474
michael@0 475 nsIFrame*
michael@0 476 nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
michael@0 477 return aFrame->GetPrevSibling();
michael@0 478 }
michael@0 479
michael@0 480
michael@0 481 nsIFrame*
michael@0 482 nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame)
michael@0 483 {
michael@0 484 nsIFrame* result = aFrame;
michael@0 485 nsIPresShell *presShell = mPresContext->GetPresShell();
michael@0 486 if (presShell) {
michael@0 487 nsIFrame* placeholder = presShell->GetPlaceholderFrameFor(aFrame);
michael@0 488 if (placeholder)
michael@0 489 result = placeholder;
michael@0 490 }
michael@0 491
michael@0 492 if (result != aFrame)
michael@0 493 result = GetPlaceholderFrame(result);
michael@0 494
michael@0 495 return result;
michael@0 496 }
michael@0 497
michael@0 498 bool
michael@0 499 nsFrameIterator::IsPopupFrame(nsIFrame* aFrame)
michael@0 500 {
michael@0 501 return (aFrame &&
michael@0 502 aFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP);
michael@0 503 }
michael@0 504
michael@0 505 // nsVisualIterator implementation
michael@0 506
michael@0 507 nsIFrame*
michael@0 508 nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame) {
michael@0 509 return aFrame->PrincipalChildList().GetNextVisualFor(nullptr);
michael@0 510 }
michael@0 511
michael@0 512 nsIFrame*
michael@0 513 nsVisualIterator::GetLastChildInner(nsIFrame* aFrame) {
michael@0 514 return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr);
michael@0 515 }
michael@0 516
michael@0 517 nsIFrame*
michael@0 518 nsVisualIterator::GetNextSiblingInner(nsIFrame* aFrame) {
michael@0 519 nsIFrame* parent = GetParentFrame(aFrame);
michael@0 520 if (!parent)
michael@0 521 return nullptr;
michael@0 522 return parent->PrincipalChildList().GetNextVisualFor(aFrame);
michael@0 523 }
michael@0 524
michael@0 525 nsIFrame*
michael@0 526 nsVisualIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
michael@0 527 nsIFrame* parent = GetParentFrame(aFrame);
michael@0 528 if (!parent)
michael@0 529 return nullptr;
michael@0 530 return parent->PrincipalChildList().GetPrevVisualFor(aFrame);
michael@0 531 }

mercurial